php like a pro – Terminally Incoherent http://www.terminally-incoherent.com/blog I will not fix your computer. Wed, 05 Jan 2022 03:54:09 +0000 en-US hourly 1 https://wordpress.org/?v=4.7.26 PHP Like a Pro: Part 7 (First Steps with Silex) http://www.terminally-incoherent.com/blog/2013/01/18/php-like-a-pro-part-7-first-steps-with-silex/ http://www.terminally-incoherent.com/blog/2013/01/18/php-like-a-pro-part-7-first-steps-with-silex/#comments Fri, 18 Jan 2013 15:16:23 +0000 http://www.terminally-incoherent.com/blog/?p=13628 Continue reading ]]> As I mentioned in the previous installment, I want to use Sixex four our new issue management tools. Why? Because it is easy to work with, lean, powerful and very testable. My composer.json file at the moment looks like this:

{
    "require": {
        "silex/silex": "1.0.*",
         "twig/twig": ">=1.8,<2.0-dev"
    },
    "minimum-stability": "dev",
    "autoload": {
        "psr-0": {
            "SITS":       "src/"
        } 
   } 
}

I think I put up a basic "Hello World" example last time, but I will do it once again for the sake of completeness. Here is a basic Silex App, but keep in mind that to get it working you need to include a few rules in your .htaccess file. I illustrated this in Part 3 of this series.

get('/', function() use($app) {
    return 'Hello World';
});

$app->run();

Yep routing in Silex is that simple. You use either a get or a post method, you specify the address pattern and you return a string that gets rendered on the page. Its very elegant and what is more important it enforces good habits. For example, when writing a Silex application there is absolutely no reason for you to ever have to use a print statement. Compare it to our pastebin where we would have to manually render our twig templates by printing them out with echo.

With Silex we can simply hand off that job to the framework. We simply build a string, and pass it on without caring what happens next. And the good news is that we can still use Twig just as easily if not easier than before. As a matter of fact it happens to be one of the built-in service providers. If I wanted to re-implement the above example using Twig templates I would simply have to add two lines of code:

register(new Silex\Provider\TwigServiceProvider(), array(
    'twig.path' => 'views',
    'twig.autoescape' => true ));


$app->get('/', function() use($app) {
    return $app['twig']->render('main.html', array('name' => "World!"));
});

$app->run();

This will render the main.html file just as before. Note the register method. This is sort of how things are done in this framework. Your $app is the central point of the entire application and also a sort of a universal wrapper. You plug services and providers into it, and then you can just pass that one object into the routing closures. This keeps the code concise - when you define a routing closure (like we just did above for the / path) you just say use($app) and you are done. That one little variable extends it's virtual tendrils throughout the entire code base and makes other things accessible in places where they otherwise wouldn't be.

Granted, you could achieve something very similar by simply using a facade pattern, and defining bunch of static factories that would be globally accessible from any scope but... Well, sometimes you want to unit test. As the name suggests the main purpose of unit testing is testing units or components in isolation. You want to see how it behaves against some know, hard coded sets of inputs and outputs without interference from other parts of the code base. Unfortunately few functions or classes can exist in programmatic void, so it is a common practice to mock up fake objects and helpers for the purpose of tests. The problem with static factories is that they are not easy to fake.

You can see this in my test suite for the Pastebin app. In Part 3 I decided to create a TwigFactory class to avoid passing around a Twig environment object, and from that point I was mostly stuck with it. Thankfully the class was only few a lines long convenience helper, so it didn't foul up my tests too much. But imagine if this was something more complex. Something with few thousand lines and an array of bugs of its own. I would be hard pressed not to include it in my tests. The Silex way gives you a live object you pass around your app, which can be easily mocked with canned set of responses for the purpose of testing.

Our Pastebin didn't have any authentication logic, but the Issue Tracker ought to have one. It's probably a good idea to get that bit out of the way first before we start building the rest of the site. Typically you handle this sort of thing using PHP's global $_SESSION array and the collection of functions that initialize and flush it. It is a rather fiddly process - you have to remember to call session_start at the top of your scripts, make sure you properly unset the session variables when the user logs out and etc.

Silex provides us with something called SessionServiceProvider which is a helper class that gets registered within your $app much like we did with twig. Once you have it registered you can use it like this:

// session provider
$app->register(new Silex\Provider\SessionServiceProvider());

$app->get('/', function() use($app)
{
    // check if logged in
    if (null === $user = $app['session']->get('user')) return $app->redirect('/login');

    return $app['twig']->render('main.html', array('name' => "Luke"));

});

The session is maintained automagically. You don't have to do anything. Want to log user out? Just do set('user', null) and you're done. It doesn't get any simpler than that. Also note how easy it is to redirect. Compare this to the usual PHP way of accomplishing things which was always to send a custom header to the browser like:

 header( 'Location: /some/page' ); 

Yeah, sending raw headers to the browser as a standard recommended practice. This is one of those terribad, awfully misguided things you can learn when you browse PHP.net documentation sometimes. Of course there are legit reasons for the header function to exist, but doing redirects is not one of them.

Also, I would like to point out one more thing - I grabbed the authentication snipped directly from the Silex example and I would like to point out that I really approve of style. Let me re-post this line just so I can talk about all the things that are absolutely right here:

if (null === $user = $app['session']->get('user'))

First, note the tipple equals identity comparison operator which no one ever remembers to use in situations like this. Second, note the unconventional assignment order. Most programmers prefer to put variable on the left, and value on the right. It's mostly a force of habit I guess, and one that is worth breaking. Why? Think about it - if you put the value on the left and variable on the right, and then somehow turn == to = you get a syntax error that can be fixed right away. If you do the same with the usual positioning, the compiler happily assumes you are doing an assignment, and keeps chugging along and you end up with a weird logical bug that will be much more difficult to find.

Finally, since in PHP assignment has higher precedence than identity comparison this will initialize $user and make it available within the scope of our routing closure. This is a lot of work accomplished by a single, concise and well written line.

How do you handle POST requests in Silex? It's equally easy. Let's say I make a login form that submits to itself via POST. Here is how I would capture that:

use Symfony\Component\HttpFoundation\Request as Request;
$app->post('/login', function(Request $request) use($app)
{
    $usr = $request->get('username');
    $pas = $request->get('password');
 
    //TODO: authenticate
 
    $app['session']->set('user', array('username'=> $usr));
    return $app['twig']->render('postLogin.html', array('username' => $usr));
 
});

Few words about first line in that snippet. Silex borrows a lot of modular components from the Symphony framework. The Request object is one of them. To effectively capture the POST request we have to use hinting in the function declaration. Without the type hint Silex gets a bit confused and bugs out. Since Symphony uses a deeply nested namespace scheme this gets kinda ugly, so I usually put that line somewhere in my index.php and then never think about it again.

My index file is getting a little bit crowded and top heavy. The pattern with Silex is to register services and providers ahead of time, so this will only get worse with time. Perhaps it would be a good idea to move all that declarative stuff to it's own little place. I know I just said that static factories are not the best idea with respect to testing but I figured making a stating bootstrap function was the best way to encapsulate the initialization of my Silex app without polluting the code with dirty include statements. The bootstrap function will basically just set up Silex environment and return a fully initialized $app so that I can simply do $app = SITS\App::bootstrap(); at the top of my index.php file. This way my index file can contain play the role of the front controller doing all the routing logic without a lot of cruft on top.

The bootstrap will look like this:

register(new \Silex\Provider\TwigServiceProvider(), array(
            'twig.path' => 'views',
            'twig.autoescape' => true
        ));

        // session provider to handle user sessions
        $app->register(new \Silex\Provider\SessionServiceProvider());

        // ### Register Custom Services ###

        // functions dealing with password hashing
        $app['crypto.helper'] = function ($app) { return new Helper\Crypto(); };

        return $app;

    }
}

Note the second to last line of this class. This is how you define custom "services". I mentioned this above, and here is a live example. I created a little Helper class called Crypto to help me handle hashing and salting passwords for our users. I will talk about its implementation in the next installment because this is a big topic, but I just wanted to show you how things get plugged into the $app variable.

At some point later I will be able to call the functions within the Crypto helper by doing:

$app['crypto.helper']->doSomething();

Before I wrap this up, here is my index.php for the sake of completeness.

get('/', function() use($app)
{
    if(null === $user = $app['session']->get('user'))
        return $app->redirect('/login');
    
    return $app['twig']->render('main.html', array('name' => $user['username']));
});

$app->get('/login', function() use($app)
{
    return $app['twig']->render('getLogin.html');
});

$app->post('/login', function(Request $request) use($app)
{
    $usr = $request->get('username');
    $pas = $request->get('password');

    //TODO: authenticate

    $app['session']->set('user', array('username'=> $usr));
    return $app['twig']->render('postLogin.html', array('username' => $usr));

});

$app->get('/logout', function() use($app)
{
    $app['session']->set('user', null);
    return $app['twig']->render('logout.html', array());

});

$app->run();

My directory structure looks like this right now, just so you can see how things are spread out, and how I'm using the PSR-0 autoloading feature in Composer to it's fullest extent:

SITS Project Directory

SITS Project Directory

Next time we will implement the Crypto helper, finish the login/logout logic and perhaps have a chance to do some other stuff as well.

]]>
http://www.terminally-incoherent.com/blog/2013/01/18/php-like-a-pro-part-7-first-steps-with-silex/feed/ 3
PHP Like a Pro: Part 6 (A New Project) http://www.terminally-incoherent.com/blog/2013/01/09/php-like-a-pro-part-6-a-new-project/ http://www.terminally-incoherent.com/blog/2013/01/09/php-like-a-pro-part-6-a-new-project/#comments Wed, 09 Jan 2013 07:14:52 +0000 http://www.terminally-incoherent.com/blog/?p=13573 Continue reading ]]> Having created a really simple Pastebin, how about we embark on something more ambitious. An actual web app with all the usual trappings: user accounts, session management, email notifications and etc. On the other hand, I shouldn’t try to build anything too ambitious because the purpose of this series is an educational one – which means I need to be able to break it down into manageable chunks and post digestible code snippets here for your enjoyment.

Oh, and I should probably introduce some new tools and concepts. If we designed this new thing using the same exact set of tools it wouldn’t be educational, no? It would be just… Work, I guess. Cause that’s what you usually do – you find a set of tools and packages you are comfortable with and you use them often. It saves time, effort and research when you have tight deadlines. But we want to learn and grow here, so let’s use something new.

The Toolkit

For this project I want to use a PHP micro-framework called Silex. Why this one? Because I think it is fairly brilliant and elegant. Here is the idea behind it: instead of providing a traditional full stack (and the kitchen sink) framework Silex is modular. At it’s very slimmest, it gives you a very neat front controller and page routing mechanism. Observe:

$app = new Silex\Application(); 

$app->get('/hello/{name}', function($name) use($app) { 
    return 'Hello '.$app->escape($name); 
}); 

$app->run();

Is that sexy or what? It is worth using just for that. But the framework has a lot more to offer. You want templating? Sixex seamlessly integrates with Twig. Remember that Twig factory class I created for the pastebin? There is a built-in equivalent in Silex. It also has a session handling module, form generation, form validation, email handling module, database abstraction layer and etc. All of these are more or less independent projects – many of which were built for the much fatter and complex Symphony framework. Silex binds them all together and ensures they work correctly.

So for this project I figured I would use the following Silex service providers:

  1. Twig
  2. Session
  3. SwiftMailer
  4. Doctrine

I actually really wanted to use Doctrine ORM, but Silex only has the integration with Doctrine DBAL. Granted, it shouldn’t be a major issue to use the ORM – it is just not officially supported, and there is little documentation for it so we would have to figure out how to fit it into place ourselves. Not sure if I want to do that, or buckle up and just go with DBAL and write a bit more SQL by hand.

Here is the reason I really like Silex: it uses Composer to manage all these dependencies. So all I need to do to get a Silex bundle with just the packages listed above and their own dependencies is to create a properly formatted composer.json file.

Here is another great thing about this framework – it is on the bleeding edge of the PHP world. It won’t actually run on anything less than PHP 5.4 because it makes extensive use of PHP Traits and improved closure syntax. It is elegant, modern, modular, sleek and very cool. I feel it is designed the way PHP apps ought to be designed.

If you happen to be running an Ubuntu 10.4 Vagrant box, like I told you to then this is what you will have to do to upgrade it to PHP 5.4:

sudo aptitude install python-software-properties
sudo add-apt-repository ppa:ondrej/php5
sudo apt-get update
sudo apt-get install php5

For some strange reason the latest version of PHP has not been back-ported to the official Lucid Lynx repositories, so we have to use a PPA.

I want to potentially showcase a build tool that would automate the testing and prepare my project for deployment. I guess I could use Ant, but since we are trying to be all about PHP perhaps Phing would be a better choice.

There is probably also a good chance we might need some JavaScript for this project. If we do, an there is enough complexity there I’m considering using jQuery framework and perhaps RequireJS for dependency management.

How does RequireJS work? Well, it makes managing and loading JavaScript much easier. All you do is insert a single line in your template like this:

Then you create main.js in the directory scripts/ and do something like this:

require(["jquery", "jquery.plugin1", "other"], function($) {
    //this will load in order 
    // - srcirpts/jquery.js
    // - scripts/jquery.plugin.js
    // - scripts/other.js
});

Granted, this is supposed to be mostly a PHP series, but these days it is actually hard to do any web development without some JavaScript in the mix. So we might as well use a little bit of it. And since we will be using it, we also ought to have some way of testing it.

I’m thinking about using either QUnit or Buster.js framework. I haven’t used either one of them so it will be a learning experience for me too.

So, having decided on the tools and environment, what kind of simple, yet non-trivial kind of app could we make?

Problem Statement

There are a lot of bug tracking apps and services out there which range from really simple embedded apps (like GitHub and Google Code bug trackers), no frills standalone fully hosted services (Like Lighthouse) to full featured, modular and extensible self hosted apps (like Trac). The choice is much slimmer when it comes to IT issue tracking. Most ticketing systems out there take the kitchen sink approach to IT and like to flaunt their long feature lists. Personally, I like simplicity so many years ago I wrote my own and called it The Issue Tracking System. My boss liked the idea, but didn’t like the acronym so he told me to drop the “The” and re-brand it with company name.

But that was long ago, and I wouldn’t show you the code that runs TITS out of fear of dying out of embarrassment. But ever since I realized how bad that app was, I wanted to re-write it from scratch, simplify it even further removing the business specific features and making it generic and release it upon the world. I want it to be as simple and easy to use as the issue tracker in Google Code.

Specifications

Pastebins are kinda intuitive – if you used one, you kinda know how it works on the basic level and everything else on top are just bells and whistles. Issue tracking systems can be very complex, or very simple depending on needs. I want to keep it simple.

So let’s think about our main data entities. I can think of three:

  • Users – we obviously need accounts for end-users, and specialists/li>
  • Issues – users open up issues, or issues are opened by technicians based on calls/emails from the users
  • Updates– if the issue is not resolved right away technicians follow-up on them by posting updates that track their progress

IT tickets tend to be a little bit different than software bugs. A bug usually affects everyone, so bug trackers are typically open to the public and searchable by anyone. A typical IT issue is something along the lines of “Judy from accounting minimized her Outlook (again) and help desk had to remote in and maximize it for her, and then help her send an email because she forgot how (again).” To save Judy some embarrassment, I’m thinking our tracking system should have access controls – users only being able to see issues they are affected by.

Let’s talk about each of these elements.

User

A user is identified by an email address and a password. I envision two types: regular users who can open new issues and post updates, specialists who can edit, prioritize and close issues. Specialist may also have an optional “title” that describes their function (ie. software support, hardware support, manager, etc).

Issue

Lets keep it as simple as possible. Here is the information I want to keep about every issue:

  • Who is having this issue?
  • When was it opened? (automatic time-stamp when record is created)
  • Who is assigned to fix it? A specialist should be able to assign it to himself (default option) or delegate it to a colleague.
  • Is it open or closed? Closed status should be either “resolved” or “rejected“. Specialists ought to be able to reopen closed issues.
  • What is the priority? (low, medium, high – defaults to medium)
  • Are there other people who need to be aware of this issue (eg. a manager, ceo, supervisor, another affected user, etc)? This could be a comma separated list of emails, set by specialist.
  • Single sentence summary.
  • Detailed description and steps to reproduce.

Additional info can be attached to each issue using tag system like the one in Github issue tracker though it does not need to be that fancy. The tags can be used to implement category system (hardware, software, email, malware, etc..). It gives the system flexibility.

Update

Updates ought to be just simple text comments with a date, and name of the user who posted it. In addition, when a specialist modifies an issue (by adding a tag, or closing it) an update should be posted with that user’s credentials and automatically generated text saying for example “so and so changed status to resolved”.

Use Cases

Or maybe user stories if you want to be more agile or something. I honestly don’t care what you call these things, but I basically want to lay down basic interactions people will have with this app. I envision about 8 separate scenarios (excluding stuff like logging in and logging out) that will help us envision the interface and life cycle of an issue.

The primary actors are User and Specialist. Users can only see issues they have opened or on which they were CC’d. Specialists can see all the issues. By default when logged in everyone sees a list of open issues they are involved in. Users can click over to see closed issues. Specialists can also see new/unassigned issues.

Note this is probably boring and mostly for my benefit so I’m going to make it small so you can just skip over it if you don’t feel like reading it:

Case #1: New Issue Submitted by User

  1. A new user logs in and sees there are no visible issues posted.
  2. User clicks a link/button to create a new issue.
  3. User fills out a form consisting of the summary and detail fields and nothing else.

Case #2: User updates an issue

  1. User logs in and sees a list of existing issues opened him her.
  2. User clicks on an issue to see detailed information and updates.
  3. User scrolls down and posts and update by filling out the update box.
  4. If issue was assigned to a specialist, he gets an email notification.

Case #3: Specialist updates an issue

  1. Specialist logs in and sees a list of issues he is assigned to.
  2. Specialist clicks on an issue to see detail.
  3. Specialist scrolls down and posts and update by filling out the update box.
  4. If the issue was unassigned it automatically becomes assigned to the specialist.
  5. User gets email notification.

Case #4: Specialist changes issue priority

  1. Specialist logs in and sees a list of issues he is assigned to.
  2. Specialist clicks on an issue to see detail.
  3. Specialist changes the priority.
  4. An auto-update is posted.
  5. User gets email notification.

Case #5: Specialist closes an issue

  1. Specialist logs in and sees a list of issues he is assigned to.
  2. Specialist clicks on an issue to see detail.
  3. Specialist sets the status to resolved or rejected.
  4. Closing date is recorded.
  5. An auto-update is posted.
  6. User gets email notification.

Case #6: Specialist claims or assigns an issue

  1. Specialist logs in and sees a list of issues he is assigned to.
  2. Specialist clicks over to see a list of unassigned issues.
  3. Specialist clicks on an issue to see detail.
  4. Specialist assigns the issue to another specialist.
  5. An auto-update is posted.
  6. Both the User and the other specialist gets email notifications.

Case #7: Specialist reopens an issue

  1. Specialist logs in and sees a list of issues he is assigned to.
  2. Specialist clicks over to see a list of closed issues.
  3. Specialist clicks on an issue to see detail.
  4. Specialist changes the status to open.
  5. Closing date is removed.
  6. An auto-update is posted.
  7. User gets an email notification.

Case #8: Help Desk Call

  1. Specialist logs in and sees a list of issues he is assigned to.
  2. Specialist clicks to create a new issue based on phone call or email from User.
  3. Specialist fills out summary, detail, email of the User, chooses priority and assigned specialist (defaults to himself).
  4. Optionally Specialist checks a box “resolved”.
  5. If the box was checked closing date is recorded.
  6. User gets email notification.

Case #9: From Email

  1. User or specialist clicks on direct URI from email notification.
  2. If not logged in, prompted to do so.
  3. Taken directly to specific issue page.

We can use these use-cases as templates to create acceptance tests with Codeception.

We have a lot of auto updates for all kinds of scenarios. It would be good to be able to visually distinguish these from actual updates posted by humans. Perhaps a field in the Update table can let us do that.

User Interface Mockups

Often it is a good idea to create UI Mockups for your page before you even start working. Simple wireframe will help you nail down the rough outline for the layout – especially if you will be working with a team of designers. I used the neat online tool called Frame Box to create these.

An end user creating an issue ought to see something like this:

User View

User View of New Issue page

A specialist will have all the options available on the sidebar like this:

Specialist View

Specialist View of New Issue Page

Here is roughly how a static (ie. non editable) detailed view of the issue would like. Specialists would see a slightly different version where they would have some controls to change the status, priority or close the issue.

Issue Detail Page

Issue Detail Page

I’m actually thinking about placing these controls right above the “update” text box – this way the process of closing an issue or changing priority would be identical to that of posting an update.

Onward

Sorry for the wall of text here, but I figured we need to jot down some specifications and make a few design decisions up-front so that we can be clear as to what we want to create and how we will be going about it. Next time, we’ll actually write some code.

]]>
http://www.terminally-incoherent.com/blog/2013/01/09/php-like-a-pro-part-6-a-new-project/feed/ 3
PHP Like a Pro: Part 5 http://www.terminally-incoherent.com/blog/2013/01/04/php-like-a-pro-part-5/ http://www.terminally-incoherent.com/blog/2013/01/04/php-like-a-pro-part-5/#comments Fri, 04 Jan 2013 06:40:05 +0000 http://www.terminally-incoherent.com/blog/?p=13454 Continue reading ]]> In Part 4, I mentioned the Codeception test framework, but didn’t really get to use it because their page temporarily went down and I didn’t feel like trying to hunt down the documentation from third party sources. That outage seemed only to last about an hour, but by that time I was already finished with my post. Besides, it gives me more stuff to talk about today.

You can install codeception via Pear like so:

sudo aptitude install php5-curl
sudo pear channel-discover codeception.com/pear
pear install codeception/Codeception

Note that it users the curl feature of PHP and hence I added that there as a prerequisite. This will make codeception available under the command codecept. Now that we have it installed globally, we have to bootstrap the test environment in our project folder:

cd /vagrant/www
codecept bootstrap
codecept build

This will create a tests folder and generate a lot of garbage inside. A clean test folder after issuing the bootstrap command usually looks like this:

Codecept Generated Fies

Codecept Generated Fies

At the moment I’m mostly concerned with acceptance testing. Codeception has a unit testing module, which is built around PHPUnit. Their idea is to make writing tests simpler. I could adopt it, but it means I would have to re-write my tests in the Codeception syntax which is essentially just an abstraction layer. At this point it is a waste of time, so I can simply get rid of the unit, and functional modules and re-organize my test directory to look like this:

Test Directory

Test Directory

As you can see, I moved my unit tests to unit/ and created bunch of new tests in acceptance/. The nice thing about these frameworks is that PHPUnit will ignore all of the Codeception code, and Codeception will ignore PHPUnit tests so they can co-exist in the same directory.

To start writing Codeception tests we need to first change the project url in the acceptance.yaml file:

class_name: WebGuy
modules:
    enabled:
        - PhpBrowser
        - WebHelper
    config:
        PhpBrowser:
            url: 'http://localhost'

Watch out for the trailing slash in the URL – if you include it, it will mess you up later.

Once this is done, we can use the Codeception framework to generate a test code stub like this:

codecept generate:cept acceptance CreatePaste

It will create a new file tests/acceptance/CreatePasteCept.php with some basic sample code. We can expand it to a full test like this:

wantTo('Create a new paste');
$I->amOnPage('/');
$I->see("Paste It");
$I->fillField('content', 'This is a triumph...');
$I->click('paste');
$I->see('Your paste is available here');

As you can see, writing Codeception tests is actually much easier than creating PHPUnit classes. The syntax is very simple, but rather powerful. It actually uses Mink framework combined with Goutte Web Scraper to emulate text browser behavior in PHP. You could use Mink directly, but Codeception provides us with a very high level layer of abstraction which makes writing tests incredibly intuitive.

The testing layer is actually driven by PHPUnit so running the tests using the command codecept run will bring up a very familiar interface:

Codeception Test

Codeception Test

Let’s create another test – this time let’s hit up a non-existing page:

codecept generate:cept acceptance VisitNonexistentPaste

The PHP code can be structured like this:

wantTo('Visit paste #99999');
$I->amOnPage('/99999');
$I->see("No such paste");

Assertions are done using the see() method, and the browsing is done using amOnPage(). The whole test takes shape of a narrative. You essentially describe what you want it to do, and it does it. Let’s do another one:

codecept generate:cept acceptance VisitPastePageDirectly

Here is the PHP code:

wantTo('Visit /paste directly with no input');
$I->amOnPage('/paste');
$I->see("Paste content cannot be empty");

You actually do not need to use the generate:cept command to create these. Here is a little secret – any file you create in the acceptance/ folder, which has a name that ends in Cept.php will be processed when you run codecept run. If you don’t want a file to be processed, do not end it’s name in Cept – it’s that easy.

Here are two more quick tests – one to submit an empty string, expecting to see an error:

wantTo('Submit an empty string');
$I->amOnPage('/');
$I->click('paste');
$I->see('Paste content cannot be empty');

And here is a test to submit the form with a null value. Note that this could never really be tested using the browser, but it could potentially happen in production due to some hidden Hisenbug that was introduced somewhere else entirely:

wantTo('Submit null value');
$I->amOnPage('/');
$I->submitForm('#paste', array('content' => null));
$I->see('Paste content cannot be empty');

Running multiple tests produces very nice output that looks like this – it’s actually prettier than the raw PHPUnit which just gives you dots as it tests, and gets kinda ugly when you set it into verbose mode:

Multiple Tests with Codeception

Multiple Tests with Codeception

Running these tests from the command line is much faster, and vastly more consistent and efficient than actually trying to manually test the application by hand via the browser. The tests always run the same, and take mere seconds whereas your manual tests may be inconsistent, and may not always cover all the important use cases.

Most of the time, you should not be writing this many tests at once. They should organically develop with your application. You get an idea how to structure your code, you write a unit test, you write the code, test it, then you write an acceptance test and plug the code into the app. Right now we’re just playing catch-up.

GeSHi Syntax Highlighter

Our Pastebin is now fully functional, but it is lacking one of the distinct features of this class of applications – code highlighting. Implementing such a feature is a non-trivial task, but fortunately someone already did it for us. The great thing about working with a mature language such as PHP is that there is a huge number of free, open source packages and projects for just about any occasion.

GeSHi is a code highlighting library. It takes a string, parses it and gives it back to you, wrapped up in HTML that colorizes it according to a set of rules defined for particular programming language. It is actually the same engine I use in this blog to colorize my code snippets, so I know it works fairly well.

Like most projects, GeSHi is available via Composer. Granted, I believe the package listed there is actually a mirror, but for our purposes it works just as well. To add it to our project we simply need to add a single line to our composer.json file:

{
    "require": {
        "twig/twig": "1.*",
        "gabordemooij/redbean": "dev-master",
        "easybook/geshi": "dev-master"
    },

    "autoload": {
        "psr-0": {
            "SillyPastebin":       "src/"
        } 
   } 
}

Now we run composer update and the code will be downloaded, and placed in vendor/ folder and registered with the Composer autoloader.

Using GeSHi is actually very simple. Let me give you an idea how it works with a little snippet of pseudo-code. This is all you really need to do to parse and output highlighted code:

$geshi = new GeSHi($some_code, $some_language);
echo $geshi->parse_code();

GeSHi output looks a bit like this:

  

I know this because I looked at the examples listed on their webpage. This is of course followed by more code wrapped in span and div elements. The HTML that is generated is a bit of a mess, but we don't really care as long as it works.

Since we know roughly how our output is going to look we can design a unit test to anticipate it. Something like this should do the trick:

    public function testShowThankYouWithValidContentAndPHPLanguage()
    {
        $content = "some content";
        $language = "php";
        $temp = $this->ctrl->addNewPaste($content, $language);
        
        $this->expectOutputRegex("/
ctrl->showPasteContent("/".$temp);
    }

We can also create an acceptance test that will submit some PHP code and test to see if it is displayed properly:

wantTo('Create a new paste in PHP');
$I->amOnPage('/');
$I->see("Paste It");
$I->submitForm('#paste', array('content' => $code, 'language' => 'php'));
$I->see("Your paste is available here");
$I->click("#");
$I->see($code, '//body/code/pre');

If we run these tests right now they will blow up. So lets modify our code to make them pass. First, lets tackle addNewPaste() method:

    /**
     * Adds new paste to the database.
     *
     * @param string $content a new paste content
     * @param string $language a GeSHi suppoerted language (default is text)
     * @throws InvalidArgumentException if $content is null or empty
     * @return integer valid pasteID (insertion id) 
     */
    public function addNewPaste($content, $language='text')
    {
        if(empty($content))
            throw new \InvalidArgumentException("Paste content cannot be empty or null");
        else
        {
            R::setup();
            $paste = R::dispense("paste");

            $paste->content = $content;
            $paste->language = $language;

            return R::store($paste);
        }
    }

I'm making $language and optional argument as not to break any of the existing code. If second argument is not present, we'll just assume that this is a plain text paste and skip highlighting. Either way, we add the new attribute to the bean and store it. Because we have never frozen our schema, RedBean automatically adjusts the paste table to accommodate the extra field.

Next, lets modify showPasteContent():

   /**
     * Renders a paste as a web page.
     *
     * @param string $uri A valid URI with a paste address
     * @throws InvalidArgumentException if the URI is invalid
     */
    public function showPasteContent($uri)
    {
        if(!$this->isValidPasteURI($uri))
            throw new \InvalidArgumentException("Invalid paste ID");
        else
        {
            $pasteID = substr($uri, 1);
            R::setup();
            $paste = R::load("paste", $pasteID);

            if(empty($paste->content))
            {
                echo $this->twig->render("nosuchpaste.html", array('pasteID' => $pasteID));
            }
            else
            {
                if($paste->language == 'text' or empty($paste->language))
                    $highlighted = $paste->content;
                else
                {
                    $geshi = new \GeSHi($paste->content, $paste->language);
                    $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS);
                    $highlighted = $geshi->parse_code();
                }
                echo $this->twig->render("show.html", array('pasteID' => $pasteID,
                                                            'content' => $highlighted,
                                                            'raw_content' => $paste->content));
            }

        }

Our Twig template will receive both the highlighted and non-highlighted copies of the paste. Why? Because we can display the non-highlighted code in a text box for easy copying, and the highlighted, enumerated code above it for easy reading.

Now we can modify the show.html template:

{% include 'header.html' %}

    

PASTE #{{ pasteID }}

{{ content|raw }}

Raw Content

new paste

{% include 'footer.html' %}

Note that I need to use content|raw in order to prevent Twig from escaping the HTML. If you recall part 3 of this series, I specifically set up Twig to auto-escape everything in order to prevent XSS issues. Since our highlighted code is chewed up and mangled by GeSHi it is probably safe to output without escaping. Besides, that's the only way to get the highlighting to actually work. If I don't display it raw, I will get bunch of HTML code in my display box.

Now comes the tricky part. We need to modify the form.html template and add a pull-down menu from which the user will be able to choose an appropriate language. I actually want this to cover all the languages that GeSHi supports. How do we do that? Well, let's start like this:

{% include 'header.html' %}

    

Paste It


{% include 'footer.html' %}

I don't believe GeSHi has a function that dumps out a list of supported languages. Fortunately we can generate one ourselves. GeSHi keeps highlighting rules for each language in its own separate file in geshi/geshi/ directory. Each of these files is named after the language it describes - so for example the file for PHP highlights is geshi/geshi/php.php. Thus we can easily generate a list of languages based on the contents of that folder:

ls -1 vendor/easybook/geshi/geshi > templates/language_select.html

Now it is time for some vim magic:

" remove file extensions
:%s/.php//g

" add html tags
:%s/^/

Now we have a lovely HTML file that will plug in directly into our form template.

Here is an interesting question: what happens if we create a paste with a totally fake language. Granted, our UI limits the user to pre-defined choices which we know will work - but what if someone just sends a POST request, or modifies UI on the client side? Will it blow up? Will it keep on chugging? I couldn't answer that so I wrote this test:

    public function testShowThankYouWithValidContentAndNonexistentLanguage()
    {
        $content = "saxophone";
        $language = "gandalf";
        $temp = $this->ctrl->addNewPaste($content, $language);
        
        $this->expectOutputRegex("/
ctrl->showPasteContent("/".$temp);
    }

It actually passed without any issues. It turns out that GeSHi is very tolerant and it will still process, and enumerate code you pass into it, even if it knows nothing about the language. In fact, the entire framework is built to be extensible - so if I wanted to I could create a highlight definition file for the gandalf language and plug it into GeSHi at a later time.

I was initially considering writing some validation code to prevent something like this from happening, but as is is, I don't really see any harm in letting it slide. If someone somehow pastes code using a non-existent language, it simply won't highlight it, but it will enumerate each line. This is not a terrible way to go.

Picture Time

I don't think I have ever shown you how the finished product looks like. I guess it is time to do that now. As they say, a screenshot is worth three chapters worth of manual... Or something like that. This is the main paste page with the neat drop-down box:

Main Page

Main Page

This is how a GeSHi processed PHP code looks like when it is retrieved from the database and pasted onto the screen:

An Actual Paste

An actual paste with GeSHi highlights.

Simple, functional, does what it needs to do. We are almost done with this project. There are only a few finishing touches that are left to do here.

Extra Validation with RedBean Models

Our PasteController does a pretty good job validating the user input (in a large part due to our test-driven approach). However if I really wanted to prevent the "gandalf" use case from happening, I would have to implement a check function. I could make it part of the PasteController but better place for it would be a model class.

RedBean lets you define models by extending RedBean_SimpleModel class and then binds them to it's beans using FUSE. So you continue using beans as normal, but when you for example attempt to store one, FUSE checks if a corresponding model exists, and then instantiates it, dynamically binds all the instance variables, and calls the update() method if it exists. That's where we would put our validation code.

So for shits and giggles, lets create a Paste model:

mkdir src/SillyPastebin/Model
touch src/SillyPastebin/Model/Paste.php

The implementation Paste.php would look something like this:


Why am I throwing an exception? Well, in cases like these, where you are dealing with high level of behind the scenes arcane magic, the best way to see something changed is to make your tests blow up in a spectacular way. My tests pass with flying colors which means I'm doing something wrong... And the thing that I'm doing wrong is actually something that I am not willing to change.

The RedBean's head developer Gabor de Mooij is not a fan of namespaces. You can actually see little passive aggressive jabs against this paradigm in the RedBean documentation. As a result RedBean is a little bit namespace blind - which works very well if you organize your code without any namespaces. Since we make extensive use of this concept we have to jerry-rig RedBean to use our own naming conventions.

Fortunately this is rather simple - we just create a new helper class:

touch src/SillyPastebin/Helper/ModelFormatter.php

The ModelFormatter.php needs to contain some boilerplate code ripped off directly from the RedBean wiki. The last line is the important one - it tells RedBean how to find our models:


By default RedBean would look for a class named Model_Paste in the global namespace. We simply change that convention and tell it to look for \SillyPastebin\Model\Paste instead.

Now we simply have to add a single line to the constructor of PasteController.php:

    public function __construct()
    {
        $this->twig = \SillyPastebin\Helper\TwigFactory::getTwig();
        R::setup();
        \RedBean_ModelHelper::setModelFormatter(new \SillyPastebin\Helper\ModelFormatter());
     }

By the way, it is probably worth noting that as I was doing this, I made another small change. As you can see I moved the R::setup(); call from the individual methods to the constructor. Since this is a static call, it actually only needs to be done once when the object is instantiated and works from that point on. So we get all our configuration in one place.

Running tests with the code like this caused the exception to be triggered as it should. Knowing that the FUSE magic has worked, we can now modify our model to include some validation code:

language))
            $this->language = 'text';

        if(empty($this->content))
            throw \InvalidArgumentException("Paste cannot be empty or null");
    }
}

I'm actually a bit too lazy to implement the GeHSi language validation right now, so I just added some extra checks. This code is mostly redundant but it helps to illustrate how this concept works. The instance variables of the bean, become available as instance variables of your model, and you can manipulate them as such. It is pretty straightforward, and you can make it as simple or as complex as you want.

You can add triggers to your model for all the different actions you can perform on a bean. Here is a decent overview that shows you how it works.

Switching over to MySQL

It's time to move away from SQLite and start using real database. So let's log into our MySQL database as root:

mysql -u root -p

Now let's create a new database, a new user, and grant that user some privileges:

create database silly_db;
create user 'silly_user'@'localhost' identified by 'some_silly_password';
grant all on silly_db.* to 'silly_user'@'localhost';

For now I gave the user all the privileges because RedBean needs to create and alter tables. Once we are ready to deploy this into production we can revoke the dangerous privileges and freeze the schema.

Next, I figured we might as well create a config file that users could edit with their DB information:

touch src/SillyPastebin/Config.php

Contents would be very basic - something like this:


Finally, here is the modified constructor of the PasteController.php:

    public function __construct()
    {
        $this->twig = \SillyPastebin\Helper\TwigFactory::getTwig();
        
        $host = \SillyPastebin\Config::mysql_host;
        $db = \SillyPastebin\Config::mysql_db;
        $user = \SillyPastebin\Config::mysql_user;
        $pwd = \SillyPastebin\Config::mysql_password;

        R::setup('mysql:host='.$host.';dbname='.$db, $user, $pwd);
        \RedBean_ModelHelper::setModelFormatter(new \SillyPastebin\Helper\ModelFormatter());
     }

In case you are curious of what kind of schema was built by RedBean, we can actually log in and snoop. Here is what I saw after running my round of unit and acceptance tests:

Paste Table

Paste Table as build by RedBean

This is actually not terrible. Choosing VARCHAR(255) for language seems excessive (although I don't thing varchar actually reserves the max space so we're probably not wasting anything), and INT(11) UNSIGNED for the primary key might be a tad small if we were planning to build a live site. Still, those are pretty good guesses. And it all happens auto-magically and behind the scenes.

Here is the good news: prior to deployment, I could go in and actually modify and optimize this table to be more to my liking, and then add a single line somewhere in my code:

 R::freeze( true );

This would freeze the shcema and prevent RedBean from changing it, forcing it to abide by whatever data types and constraints I put onto it.

Conclusion

This would be it. I believe I hit all the important lessons I wanted to impart on you. We have built a fully functional pastebin in under a thousand lines of code (including unit and acceptance tests) using industry's best practices and bunch of popular third party tools. We used a test driven approach and used two popular testing frameworks to do extensive unit and acceptance testing on our code. We used a runtime dependency manager to ensure the code we used is portable and that we do not need to commit third party code to our repository. We conformed to the PSR-0 and learned about PHP autoloading. All in all I hope this was a good learning experience.

Wait... How do I know how many lines of code we wrote? I checked:

Lines of Code

Lines of Code

Note that the WebGuy.php class was auto-generated by Codeception, so those 614 lines can be subtracted from the total, since we did not write them.

I hope this series helped to illustrate that despite it's numerous flaws, PHP is not a completely terrible language. You can use it to write organized, manageable and aesthetically pleasing code. It just takes a little bit of effort. If you would like to embark on another journey into the land of PHP with me at some point in the future, let me know in the comments.

If you are interested, code for Silly Pastebin is available on GitHub.

]]> http://www.terminally-incoherent.com/blog/2013/01/04/php-like-a-pro-part-5/feed/ 5 PHP Like a Pro: Part 4 http://www.terminally-incoherent.com/blog/2013/01/02/php-like-a-pro-part-4/ http://www.terminally-incoherent.com/blog/2013/01/02/php-like-a-pro-part-4/#comments Wed, 02 Jan 2013 15:04:53 +0000 http://www.terminally-incoherent.com/blog/?p=13388 Continue reading ]]> In the previous installment we built a semi-working skeleton of our application. It is time to finally get down to the business and make it talk to the database and actually save and retrieve out content. Normally this would be the place when you buckle down, whip out your E/R diagram and start writing SQL queries to build your database. But, we are going to be using RedBean ORM for this project.

What does that mean? That means the amount of actual SQL we will need to write is very, very small. In fact, I believe I won’t need to write a single line of SQL until part five of this series. So instead of writing two thousand words about database modeling, I’m going to jump straight in and write some tests for my yet undefined method addNewPaste(). They are going to look like this:

    public function testAddNewPasteWithValidString()
    {
        $content = "some text to be pasted";
        $temp = $this->ctrl->addNewPaste($content);
        $this->assertNotNull($temp);
        $this->assertInternalType('integer', $temp);
    }

    public function testAddNewPasteWithNull()
    {
        $content = null;
        $temp = $this->ctrl->addNewPaste($content);
        $this->assertNull($temp);
    }

My first test attempts to add a new record with actual string content and expects an integer (insertion ID) in return. The second test sends in null as an argument and expects a null back. In other words we want our application to ignore null input, and return a null instead of an integer.

Here is the implementation – pay close attention because ReadBean is going to blow your mind:

use RedBean_Facade as R;

    public function addNewPaste($content)
    {
        R::setup();
        $paste = R::dispense("paste");
       
        $paste->content = $content;

        return R::store($paste);
    }

Yes, this code is actually going to work. If you never seen RedBean at work this probably bears some explaining.

The first line, binds RedBean_Facade to R. Why? Because all of ReadBean documentation uses R notation, but R is not actually a real class. If you download RedBean from the official website you get a single file which is essentially the whole package, compressed and minified, with the generated R class acting as the user interface. When you install RedBean via Composer you don’t bet the R class so you have to add this one line. It is a quirk of the framework, and you just have to remember to do it.

The setup line connects to the database. The dispense line gives you an interactive object/model – RedBean calls these objects “beans”. You manipulate the bean, and then you store it. The database schema is automatically altered to fit the data you are inserting (though there is an option to freeze the schema when you are ready to deploy into production).

Note that there is no SQL here. All is done at a very high level, and RedBean does sanitize your input. Granted, there is no reason why we shouldn’t pre-sanitize it just in case, but we can get to that later. Right now I just want to have some working code here.

You might be asking yourself, how does RedBean know about our MySQL database? Well, right now, it does not know about it. Right now it is using SQLite which is it’s goto database environment.

By the way, if the above code blows up on you saying you’re missing some drivers you’ll probably need to install sqlite support for php:

sudo aptititude install sqlite php5-sqlite php5-mysql

By default, RedBean stores the SQLite database file in /tmp/red.db. This is great for testing because we can easily wipe this database after each test. In fact, I added the following to my PasteControllerTest just to ensure that every unit test starts with a clean DB:

    public static function tearDownAfterClass()
    {
        if(file_exists("/tmp/red.db"))
            unlink("/tmp/red.db");
    }

Once we are done with the development phase and we are ready to have a more permanent storage we will simply need to modify the setup line, passing mysql hostname, username and password as arguments.

This is sort of the beauty of RedBean – it is absolutely hassle free. I can start building a complex application without actually worrying much about my database. Then once I have it done, I can simply connect it to MySQL, Postgess or whatever else the framework supports.

Let’s run our test now, shall we:

RedBean Test

RedBean Test

Oops, failed. Why? Well, RedBean is happily inserting null values into the database. This is a little bit of a tradeoff – if I defined my database up front, I would have a database error here. Since I didn’t RedBean is assuming I want null values in my DB (a completely valid assumption). In this case, data validation is on me.

Here is something to ponder: is returning null really the best way to handle this situation? Technically, null or empty pastes should not happen so perhaps it is better to throw an exception with an appropriate message?

I’m thinking about clarity and maintainability here. If someone else decides to use my method and it unexpectedly returns null, it may cause unexpected failures down the road. Null value can pass through layers of logic, and bubble up to somewhere else entirely where it blows the application up. If the method throws an exception it fails immediately with a clear and explicit error message.

Granted, if the null return value is well documented a programmer should be able to handle it. In fact, handling an null return value is less hassle than handling an exception. So this is a rather complex idea. Both approaches have their benefits and downsides.

I guess for us it is better to err on the side of caution, fail fast, and fail early. So let’s rewrite our test to assert exception rather than a null value:

    /**
     * @expectedException        InvalidArgumentException
     */
    public function testAddNewPasteWithNull()
    {
        $content = null;
        $temp = $this->ctrl->addNewPaste($content);
    }

Here is the corrected code with some data validation:

    /**
     * Adds new paste to the database.
     *
     * @param string $content a new paste content
     * @throws InvalidArgumentException if $content is null or empty
     * @return integer valid pasteID (insertion id) 
     */
    public function addNewPaste($content)
    {
        if(empty($content))
            throw new \InvalidArgumentException("Paste content cannot be empty or null");
        else
        {
            R::setup();
            $paste = R::dispense("paste");

            $paste->content = $content;

            return R::store($paste);
        }
    }

We can run the tests again and observe that valid entries are inserted to the DB, while the null or empty ones throw an exception as they should.

Let’s write a few tests for the showPasteContent method now:

    public function testShowPasteContentWith1()
    {
        $this->expectOutputRegex("/some text to be pasted/");
        $this->ctrl->showPasteContent("/1");
    }

    /**
     * @expectedException        InvalidArgumentException
     * @expectedExceptionMessage Invalid paste ID
     */
    public function testShowPasteContentWithInvalidUri()
    {
        $this->ctrl->showPasteContent("foo");
    }

    public function testShowPasteContentWith9999()
    {
        $this->expectOutputRegex("/No such paste/");
        $this->ctrl->showPasteContent("/9999");
    }

First test tries to render a paste that ought to be there already (we have inserted it in the previous test). The second test tries to use an invalid URI and expects an exception. The third one picks an ID I do not expect to exist in the database and expects to see an error message.

We haven’t made a Twig template for displaying the pastes yet so here it is:

{% include 'header.html' %}

    

PASTE #{{ pasteID }}

{{ content }}

Raw Content

new paste

{% include 'footer.html' %}

Here is the actual implementation:

    public function showPasteContent($uri)
    {
        if(!$this->isValidPasteURI($uri))
            throw new \InvalidArgumentException("Invalid paste ID");
        else
        {
            $pasteID = substr($uri, 1);
            R::setup();
            $paste = R::load("paste", $pasteID);

            $template = $this->twig->loadTemplate('show.html');
            echo $template->render(array('pasteID' => $pasteID,
                                            'content' => $paste->content));

        }

As you can see, fetching the data from the DB using RedBean is equally easy. You issue a load command, and you get a bean back. Then you can access the bean’s public fields to get at the data.

Let’s run another quick test:

PHPUnit test

PHPUnit test

Whoops. It seems that RedBean will return back a bean for non-existent entries without making much of a fuss. Once again we have to add some validation code.

But firs,t lets make a new template to handle the condition when the requested paste is not in the database:

{% include 'header.html' %}

    

No such paste

Sorry, paste with id #{{ pasteID }} does not exist or has been removed.

new paste

{% include 'footer.html' %}

Let’s fix our PHP code to actually handle the condition:

    /**
     * Renders a paste as a web page.
     *
     * @param string $uri A valid URI with a paste address
     * @throws InvalidArgumentException if the URI is invalid
     */
    public function showPasteContent($uri)
    {
        if(!$this->isValidPasteURI($uri))
            throw new \InvalidArgumentException("Invalid paste ID");
        else
        {
            $pasteID = substr($uri, 1);
            R::setup();
            $paste = R::load("paste", $pasteID);

            if(empty($paste->content))
            {
                echo $this->twig->render("nosuchpaste.html", array('pasteID' => $pasteID));
            }
            else
            {
                echo $this->twig->render("show.html", array('pasteID' => $pasteID,
                                                            'content' => $paste->content));
            }

        }

    }

You have probably noticed that I used the shorter, more succinct version of the Twig render method in the code above. This is perfectly valid, and actually more convenient in this particular case where we need to render two different templates based on a conditional.

One more quick unit test to verify we haven’t messed it up:

This time we pass with flying colors

This time we pass with flying colors

Up until this moment, I have been mostly relying on unit testing to make sure my code works. This is actually an excellent practice because it lets you concentrate on particular area of your code, and make sure it works in isolation before you integrate it with the rest of the code.

I wanted to take this opportunity to talk about the rather interesting integration testing framework called Codeception but unfortunately it appears that they’ve done broke themselves:

I swear, it wasn't me

I swear, it wasn’t me

So, I guess we’ll push the the integration testing discussion and examples until next installment. Unfortunately this means we have to test our application like ordinary chumps: using a web browser and actually typing crap in.

The test procedure ought to be something like this:

  1. Navigate to /
  2. Type in some garbage, hit submit
  3. Follow the link on the page to see the paste on it’s own page
  4. Hit some random URI to see “no such paste” message
  5. Hit /paste manually to see an error message

Step 3 reveals I haven’t actually implemented the “thanks for posting” style message. When a paste is submitted, all the user gets is a blank page. Let’s fix that.

We’re going to create a new method in PasteController and call it showThankYou(). What is it going to do? Among other things I want it to include a phrase like “Your paste is available here [LINK]”.

So here is a test:

    public function testShowThankYouWithValidContent()
    {
        $content = "some content";
        $temp = $this->ctrl->addNewPaste($content);
        
        $this->expectOutputRegex("/Your paste is available here: #$temp<\/a>/");     
        $this->ctrl->showThankYou($temp);
    }

Here is a template:

{% include 'header.html' %}

    

Thank You

Your paste is available here: #{{ pasteID }}

{% include 'footer.html' %}

And here is the implementation:

    /**
     * Renders a thank-you message with a link to a newly created paste
     *
     * @param integer $pasteID - a valid paste ID
     */
    public function showThankYou($pasteID)
    {
        echo $this->twig->render("thanks.html", array('pasteID' => $pasteID));
    }

Granted, this won’t do anything until we modify our index file:

if($uri == '/') {
    $pasteCtrl->showPasteForm();
} elseif($uri == "/paste") {
    $pasteID = $pasteCtrl->addNewPaste($_POST["content"]);
    $pasteCtrl->showThankYou($pasteID);
} elseif($pasteCtrl->isValidPasteURI($uri)) {
    $pasteCtrl->showPasteContent($uri);
} else {
  $errorCtrl->show404();
}

I also noticed that step 5 blows the application up. There is no code in my application to handle the situation when someone accesses the post url directly. To fix that we need four things. First, lets write some tests to define how our error is going to behave:

    public function testShowGenericErrorWithValidMessage()
    {
        $this->expectOutputRegex("/my test message/");
        $this->err->showGenericError("my test message");
    }

    public function testShowGenericErrorWithNull()
    {
        $this->expectOutputRegex("/Unexpected error./");
        $this->err->showGenericError(null);
    }

Note the second test – I bet that if I wasn’t starting off by writing unit tests, I would completely skip this condition because there is just no easy way to induce it simply by using the web interface.

Next we need the template:

{% include 'header.html' %}

Error

{{ errorMessage }}

{% include 'footer.html' %}

Finally, the implementation which goes in my front controller block:

if($uri == '/') {
    $pasteCtrl->showPasteForm();
} elseif($uri == "/paste") {
    try
    {
        if(!empty($_POST["content"]))
        {
            $pasteID = $pasteCtrl->addNewPaste($_POST["content"]);
            $pasteCtrl->showThankYou($pasteID);
        }
        else
            $errorCtrl->showGenericError("Paste content cannot be empty.");
    }
    catch (InvalidArgumentException $e)
    {
        $errorCtrl->showGenericError($e->getMessage());
    }
} elseif($pasteCtrl->isValidPasteURI($uri)) {
    $pasteCtrl->showPasteContent($uri);
} else {
  $errorCtrl->show404();
}

Our application is almost complete. Now we just need to add some finishing touches – like actually connecting our application to MySQL database, and freezing the DB schema.

In the first installment of this series I talked about the MVC pattern a lot, but in the end we haven’t really implemented any models. That’s mostly because we opted for RedBean which aims to make things simple and reduce the amount of typing you must do. If we went with Droctrine models would be among some of the first things we would have to build.

Still, RedBean does support the concept of models. We can create objects that will be auto-matically connected to beans and which we can use to do some extra validation, error checking and sanitization on our values. I want to accomplish that in the next part.

I also really want to try bringing in Codeception or similar acceptance testing framework and try to automate these “browse to the webpage and mess with it” tests. Perhaps when I sit down to write the next installment their website will be back.

Finally, a pastebin without code highlighting is kinda weak. So I want to see if I could pull in a third party syntax highlighting package like GeSHi and make it play nicely with our code.

]]>
http://www.terminally-incoherent.com/blog/2013/01/02/php-like-a-pro-part-4/feed/ 3
PHP Like a Pro: Part 3 http://www.terminally-incoherent.com/blog/2012/12/26/php-like-a-pro-part-3/ http://www.terminally-incoherent.com/blog/2012/12/26/php-like-a-pro-part-3/#comments Wed, 26 Dec 2012 15:01:00 +0000 http://www.terminally-incoherent.com/blog/?p=13343 Continue reading ]]> Welcome to the third installment of PHP-ing like a productive person. It’s code writing time. Here is how I like to start:

touch index.php

Yes, I’m a toucher. Sue me!

The Front Controller

The index file seems like a good place to start, right? That’s the first place your browser will hit. This is the central point of your application. And just for kicks, I will designate this file to be my front controller. What does that mean? It means that it will do all the routing. It will capture the GET requests from the visitors and then send them where they need to go.

One of the things I want in this pastebin incarnation are nice URL’s. This, is what I consider an ugly URL:

http://example.com/?paste=2345

On the other hand, this is what I consider a nice(er) URL:

http://example.com/2345

What’s the difference here? Well, the difference is in how Apache understand these two requests. The first one means “Yo, Apache, bring me index.php – I have a GET request for him”. The second means “Yo, Apache – I want to go to the 2345 folder and see what’s there!”

In other words, if you use the second URL Apache will try to find an index file in /vagrant/www/2345 directory. We don’t want that. We want our own application handle all the routing in hourse so to speak. How do we accomplish that?

We tell apache to screw off – that’s how. There are two ways to do this. In our vagrant server the easiest way to get it done is to use Apache ModRewrite. First, let’s enable it:

sudo a2enmod rewrite

Next let’s upgrade out site config:

sudo vi /etc/apache2/sites-available/project

In that file we need to add 5 new lines under the Directory heading. Everything under allow from all is new:


    Options Indexes FollowSymLinks MultiViews
    AllowOverride None
    Order allow,deny
    allow from all
    
    # enable the rewrite engine
    RewriteEngine On
    RewriteBase /
    
    # ignore file requests like /foo.php
    RewriteCond %{REQUEST_FILENAME} !-f
    
    # ignore directory requests like /foo/
    RewriteCond %{REQUEST_FILENAME} !-d

    # route stuff like /foo.php or /foo to index.php
    RewriteRule . /index.php [L]

Or you can add the same lines in a .htaccess file in your project directory. The above method is considered a bit safer, but you do what you must.

Once you add these lines and restart Apache, all requests will be re-routed to index.php. Right now now that file is blank so there is nothing exciting going on yet. So let’s change that.

showPasteForm();
} elseif($uri == "/paste") {
    $pasteCtrl->addNewPaste();
} elseif(PasteController::isValidPasteURI($uri)) {
    $pasteCtrl->showPasteContents($uri);
} else {
  $pasteCtrl->show404();
}

First line of the code includes Composer’s autoloader. This will allow me to seamlessly use the Twig and RedBean templates without actually having to explicitly include them anywhere. You’ll see this in action in just a minute.

Don’t worry about PasteController.php. It isn’t a thing yet. It does not exist. I just made it up. This code right will bug the fuck out if you try to access it via browser but that’s ok. What I’m doing here is sort of structuring the code in my head and putting it down. Having written all these calls to the non-existent methods gives me a pretty good idea what this new class ought to do:

  • If the uri is / then it will show the blank form
  • If the uri is /paste it will attempt to handle submitted data
  • If it is something else, it will test if the URL is a valid paste number and show associated paste
  • Otherwise, it will display an error

Let’s create the controller then!

Building the first class

I have a feeling you’re going to be sick of me touching things before this is done:

touch PasteController.php

I already know the basic layout of this class because I just drafted it out in my index class. All I have to do is to write it down now:


I added the the "hello world" line so that I can verify my code is working. I can do that by navigating to http://localhost:8080 on my host OS. If I nothing is amiss I should see the age-old one-line greeting echoed back to me.

Creating Twig Templates

Hello world is nice, but I want to make something real. In the first installment of this series I mentioned that echoing HTML from PHP scripts is not the greatest practice. So I want to do this right. I want to set up some Twig templates:

mkdir templates
touch templates/header.html
touch templates/form.html
touch templates/footer.html

Inside header.html I put... Well, header stuff:



    
        {{ title }}
        
    
    

The {{ title }} bit is where we are going to pass the page title. In case you haven't noticed this is the Twig template syntax. The form.html file is equally simple:

{% include 'header.html' %}

    

Paste It


{% include 'footer.html' %}

Yep, I'm importing the header and footer files. Twig will assemble all of it for me into one nice file and I haven't used a single line of PHP in these templates. This is rather elegant, don't you think?

So, now lets twig it up in our controller:

    // this is my twig instance
    private $twig;

    public function __construct()
    {
        // create twig environment and tell it where the templates live
        $loader = new Twig_Loader_Filesystem('templates');

        // set up a twig instance, enable auto-escaping
        $this->twig = new Twig_Environment($loader, array('debug' => true, 
                                                            'autoescape' => true, 
                                                            'auto_reload' => true));
    }

    public function showPasteForm() 
    {
        // load the form template
        $template = $this->twig->loadTemplate('form.html');

        // render and pass in the title at the same time
        echo $template->render(array('title' => 'Paste It'));
    }

Note, no include statements but Twig works! That's the magic of auto loading.

This is more or less all there is to Twig. It takes two lines to set it up, and another line or two to actually use it. There is really no reason not to include this library in your projects.

Testing

Seeing how we have our first class taking shape, it is probably a good idea to start building our first unit test:

mkdir tests
touch tests/PasteControllerTest.php

To make a test we extend the PHPUnit_Framework_TestCase class like this:

ctrl = new PasteController();
    }

    public function testIsValidPasteURI()
    {
        $temp = $this->ctrl->isValidPasteURI("/234");
        $this->AssertTrue($temp);
        
        $temp = $this->ctrl->isValidPasteURI("/foo");
        $this->AssertFalse($temp);

        $temp = $this->ctrl->isValidPasteURI("/foo/434");
        $this->AssertFalse($temp);

        $temp = $this->ctrl->isValidPasteURI("234");
        $this->AssertFalse($temp);

    }
}

We can run it by issuing the command:

phpunit UnitTest tests/PasteControllerTest.php

As you can see I'm testing the "easily testable" method isValidPasteURI. Why? Because it actually returns a value. These sort of methods are easy to unit test because you can just call them repeatedly with a wide variety of test values, and then make assertions about the results. Methods which produce side-effects and return no values (like showPasteForm) are little less testable.

The results should look a bit like this:

Failed Unit Test

Failed Unit Test

Why did this test fail? Well, we made our isValidPasteURI method always return true... My test expects it to return false with some values, and true with others. This was exactly what we should have expected. Now I can go back and fix the method so that it passes all my tests.

We know that a REQUEST_URI will need to start with a / and be followed by a number so here is a possible implementation:

     /**
     * Returns true if $uri contains a valid (numeric) paste id.
     *
     * A valud URI should looke like /234 - anything else is not valid
     *
     * @param string $uri a REQUEST_URI value that should be numeric
     * @return boolean tru if URI is valud, false if it is not
     */
    public static function isValidPasteURI($uri)
    {
        // if starts with / 
        if(strpos($uri, "/") === 0)
        {
            $tmp = substr($uri, 1);

            if(is_numeric($tmp))
                return true;
        }

        return false;
    }

Is this correct? I don't know, let's see:

Passed Unit Test

Passed Unit Test

As far as my Unit Test is concerned, the method performs acceptably. Is my Unit Test correct? Is it exhaustive enough? Probably not, but I can add to it later if I feel extra vigilant.

How do we test methods in which the main functionality is producing side effects? Good question. It seems that we should at least make an effort to test our showPasteForm which renders a Twig template and sends it to the browser. How do we make assertions about output though?

Well, if you just call the method in your test and run phpunit with --verbose --debug attributes, it will dump the raw HTML to the console and you can eyeball it. But that's not really testing - that's checking. We want to test! Sadly, checking output is not really what Unit Testing was designed for.

To test this function we would ideally want to emulate what the browser is doing - so generate a fake request to index.php and see if a form is displayed by patterm matching the HTML. That's not unit testing though. That's acceptance testing, and there are actually frameworks that let us do that - like Codeception for example. But I want to concentrate on unit tests now, which test elements of your code in isolation.

Fortunately PHPUnit has some functionality that can help us test this method. There is a near little assertion named expectOutputRegex. As you can probably imagine it will do some pattern matching on the output. This is how you use it:

    public function testShowPasteForm()
    {
        $this->expectOutputRegex("/Paste It<\/title>/");
        $this->ctrl->showPasteForm();
    }</pre>
<p>This is little backwards from all other tests because you assert firsts and run code afterwards, but it works. The downside of this is that you can usually only make one assertion per test. If you assert multiple things, only the last one will actually be tested. So you better pick a good regex.</p>
<p>What I want to know when this test runs is that Twig assembled my templates correctly. If you remember, in the header template the title was not defined. It contained a variable. In addition the form template did not have a title tag - it imported it. So the only way for the rendered page to have a title, and a correct one is for Twig to have worked correctly. Hence, that's what I'm pattern matching in this test.</p>
<h3>Error Handling</h3>
<p>Let's handle that pesky error situation when someone inadvertently tries to access a URI that is not a valid paste address. For example navigating to <samp>/foo</samp> should produce a 404 error code so that browsers know that nothing is there. We already have a method for that in our PasteController but... Well, is that a good place for it? Why a controller related to paste stuff should handle routing errors?</p>
<p> How about we make an <samp>ErrorController.php</samp> class and encapsulate that functionality away:</p>
<pre lang="php"><?php

class ErrorController
{
    public function show404()
    {
        header('HTTP/1.0 404 Not Found');
    }
}</pre>
<p>This works, but it doesn't really display any meaningful message to the user. The browser knows there was a 404 error, but the user sees a blank page. It would be better if we could pull in our templating engine and display a nicely formatted error message.</p>
<p>But... Well, this is not really an issue but I already initialized Twig and set it up to my liking in PasteController. Now I will have to do it again. And if I define another controller, I will have to do it there too. I know it's only two lines but that's not the case. I'm worried about the configuration options (like enabling the auto-escaping feature). I want it to be consistent throughout my application.</p>
<p>So let me make a <samp>TwigFactory</samp>! It will be a static class that will give you and instance of Twig environment pre-configured to your liking. Both Paste and Error classes will be able to grab it whenever they need it:</p>
<pre lang="php"><?php

class TwigFactory
{
    public static function getTwig()
    {
        $loader = new Twig_Loader_Filesystem('templates');
        return new Twig_Environment($loader, array('debug' => true, 
                                                    'autoescape' => true, 
                                                    'auto_reload' => true));
    }
}</pre>
<h3>Refactoring</h3>
<p>My project directory is getting messy. I have controller classes floating around, I have helper classes (like the TwigFactory) and all kinds of other stuff in there. I think it's time to do some cleanup. It is usually a good idea to commit your existing code right before you're going to make major changes:</p>
<pre lang="bash">git add .
git commit</pre>
<p>This way if we royally mess something up, we can quickly roll back to the last known good state for the project and start over. Now we can proceed with potentially destructive refactoring actions:</p>
<pre lang="bash">mkdir controllers
mkdir helpers
mv *Controller.php controllers/
mv *Helper helpers/</pre>
<p>This should actually make our working environment much cleaner. Observe:</p>
<div id="attachment_13377" style="width: 374px" class="wp-caption aligncenter"><a href="http://www.terminally-incoherent.com/blog/wp-content/uploads/2012/12/directory.png"><img src="http://www.terminally-incoherent.com/blog/wp-content/uploads/2012/12/directory.png" alt="Project Directory Structure" width="364" height="362" class="size-full wp-image-13377" srcset="http://www.terminally-incoherent.com/blog/wp-content/uploads/2012/12/directory.png 364w, http://www.terminally-incoherent.com/blog/wp-content/uploads/2012/12/directory-150x150.png 150w" sizes="(max-width: 364px) 100vw, 364px" /></a><p class="wp-caption-text">Project Directory Structure</p></div>
<p>Now that I cleaned everything up I will need to go back and correct the include statements everywhere and then re-test everything. Which reminds me, we should build a unit test for Error Controller as well. Good news is that we can crib most of it from the Paste Controller test:</p>
<pre lang="php"><?php
require_once "vendor/autoload.php";
require_once "controllers/ErrorController.php";
require_once "helpers/TwigFactory.php";

class ErrorControllerTest extends PHPUnit_Framework_TestCase
{
    protected $err;

    protected function setUp()
    {
        $this->err = new ErrorController();
    }

    public function testObjectInitialization()
    {
        $this->assertObjectHasAttribute("twig", $this->err);
    }

    public function testShowPasteForm()
    {
        $this->expectOutputRegex("/<h1>404<\/h1>/");
        $this->err->show404();
    }
}</pre>
<p>Now that I have multiple tests I can actually run them together by simply pointing PHPUnit at my test directory like this:</p>
<div id="attachment_13380" style="width: 557px" class="wp-caption aligncenter"><a href="http://www.terminally-incoherent.com/blog/wp-content/uploads/2012/12/tests.png"><img src="http://www.terminally-incoherent.com/blog/wp-content/uploads/2012/12/tests.png" alt="Running Multiple Tests" width="547" height="293" class="size-full wp-image-13380" /></a><p class="wp-caption-text">Running Multiple Tests</p></div>
<h3>Auto Loading and Code Standards</h3>
<p>I just realized something... I've been doing this wrong. I'm kinda blogging along as I assemble this code, and it suddenly struck me that I am not using one of the great features that comes with Composer - auto-loading. Or rather, I'm using it for all the third party classes, but I still have to "require" my own classes. This is silly.</p>
<p>But alas, to actually leverage this feature my code should conform to PSR-0... Which it does not. It is not properly namespaced, and not organized the way it should be. So we need to do even more refactoring. Best do it now, while the code-base is still small and uncomplicated.</p>
<p>The way Composer's autoloading works is that you can specify your own namespace in the <samp>composer.json</samp> like this:</p>
<pre lang="javascript">
    "autoload": {
        "psr-0": {
            "SillyPastebin":       "src/"
        } 
   }</pre>
<p>Here I'm telling composer to look for <samp>SillyPastebin</samp> code in the <samp>src/</samp> directory. This directory does not exist yet. I will have to create it.</p>
<p>Once I have this directory in place, the rest is just a pattern matching game. When you instantiate a namespaced class like this:</p>
<p><samp>SillyPastebin\Namespace\SomeClass</samp> </p>
<p>the composer autoloader simply converts all the php namespace delimiters (these things: <samp>\</samp>) into system path delimiters (in our case <samp>/</samp>) using the directory you specified in the config file as root. So if it sees the invocation I shown above it will attempt to auto-load a class located at:</p>
<p><samp>/src/SillyPastebin/Namespace/SomeClass.php</samp></p>
<p>This is not how my code is organized. I have controller and helper folders in my top level directory and nothing is namespaced. With the code as it is, there is simply no way for me to leverage this great feature. Which means we have some housekeeping and refactoring to do. </p>
<p>First let's move the files around to conform to these conventions:</p>
<pre lang="bash">mkdir src
mkdir src/SillyPastebin
mv controllers/ src/SillyPastebin/Controller
mv helpers/ src/SillyPastebin/Helper</pre>
<p>When finished, my directory structure will look like this:</p>
<div id="attachment_13382" style="width: 452px" class="wp-caption aligncenter"><a href="http://www.terminally-incoherent.com/blog/wp-content/uploads/2012/12/directory2.png"><img src="http://www.terminally-incoherent.com/blog/wp-content/uploads/2012/12/directory2.png" alt="Organizing for PSR-0" width="442" height="336" class="size-full wp-image-13382" /></a><p class="wp-caption-text">Organizing for PSR-0</p></div>
<p>Next let's refactor our code:</p>
<pre lang="php"><?php namespace SillyPastebin\Controller;

class PasteController
{
    private $twig;

    public function __construct()
    {
        $this->twig = \SillyPastebin\Helper\TwigFactory::getTwig();
    }</pre>
<p>And:</p>
<pre lang="php"><?php namespace SillyPastebin\Controller;

class ErrorController
{
    private $twig;

    public function __construct()
    {
        $this->twig = \SillyPastebin\Helper\TwigFactory::getTwig();
    }</pre>
<p>And:</p>
<pre lang="php"><?php namespace SillyPastebin\Helper;

class TwigFactory
{
    public static function getTwig()
    {
        $loader = new \Twig_Loader_Filesystem('templates');
        return new \Twig_Environment($loader, array('debug' => true, 
                                                     'autoescape' => true, 
                                                      'auto_reload' => true));
    }
}</pre>
<p>Note that I had to prefix the Twig classes with <samp>\</samp> to indicate they are not part of the <samp>SillyPastebin\Helper</sam> namespace.</p>
<p>Finally here is how you change the index:</p>
<pre lang="php"><?php require "vendor/autoload.php";

$uri = $_SERVER['REQUEST_URI'];

$pasteCtrl = new SillyPastebin\Controller\PasteController();
$errorCtrl = new SillyPastebin\Controller\ErrorController();</pre>
<p>We'll also need to refactor our tests the same way, but I'm not gonna show that because it's basically more of the same. You remove the require statements, and you add <samp>SillyPastebin\Namespace</samp> in front of all the class instatioation calls and that's about it.</p>
<p>Last thing to do is to update our composer config to make sure the auto-loading feature works:</p>
<pre lang="bash">composer update</pre>
<p>The whole thing took about five minutes, and I achieved something really cool - I will never, ever have to write another require or include statement for this project. The first line of <samp>index.php</samp> is actually the only include that I will ever need from now on. This is very, very cool and I get this for free with Composer as long as I namespace my code the right way... Which is what I should be doing anyway.</p>
<h3>Lessons Learned</h3>
<p>I started this series with the best intentions - I wanted to write this thing right. I wanted to practice what I preached in Part 1, and yet when I sat down and started writing code bad habits started sneaking in. This happens all the time - you forget, or you willingly ignore some rule or best practice. At first it works out fine, but then as you continue working things start to crumble. The code starts looking subtly wrong.</p>
<p>You know what reeled me back onto the correct path again? Unit tests. No seriously, scroll back a few paragraphs and look at the code I posted for <samp>ErrorControllerTest</samp> class. I saw these lines piling up on top:</p>
<pre lang="php">require_once "controllers/ErrorController.php";
require_once "helpers/TwigFactory.php";</pre>
<p>Here is what I realized: I may have more helper classes in the future. Also Models, which we didn't even talk about yet. I will have to include every single one of them, on every single unit test. That's silly. If I had to do this once in my index file, I'd probably be fine with it - but now I saw dozens of unit tests, with long, long lines of includes on top, and it looked wrong.</p>
<p>And so, I ended up refactoring my code to conform to the PSR-0 standard, and to use proper name spacing. Unit testing saved the day once again, and not by detecting errors, but by forcing me to think about how I structure my code. I mentioned this in part one - that's the hidden value of Unit Tests. They make you think not only about what your code is doing, but also how it is doing it.</p>
<p>This is the primary reason why I didn't go back and rework this post to look as if I meant to do it this way all along. It would probably be clearer, but I wanted to showcase how even if you have best intentions you can get easily lulled into complacency and how following some best practices and using the right tools can jolt you right back onto the correct path.</p>
<p>Next time I'll try to finally get some database interaction accomplished using RedBean. Unless I get side-tracked again.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.terminally-incoherent.com/blog/2012/12/26/php-like-a-pro-part-3/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>PHP Like a Pro: Part 2
		http://www.terminally-incoherent.com/blog/2012/12/19/php-like-a-pro-part-2/
		http://www.terminally-incoherent.com/blog/2012/12/19/php-like-a-pro-part-2/#comments
		Wed, 19 Dec 2012 15:04:11 +0000
		
		
		http://www.terminally-incoherent.com/blog/?p=13305
		Continue reading ]]>
				In part one of this series I imparted some serious knowledge and discussed a lot of theoretical bullshit. Not all of it went over well, and not all of it was PHP specific. A lot of the stuff we talked about was just general programming advice. This part will be more concrete and hands on. Before we start, let’s make sure we are all on the same page. Lets talk about your stack.

The development machine you are sitting on right now – is it running a LAMP stack? Or is it MAMP? Or worse, WAMP, WIMP or some other abomination?

Here is something to chew on: chances are you are going to be deploying your shitty PHP app (all software is shitty, not just yours so don’t take this personal) to a web host that is running LAMP stack. Even though PHP ought to run the same everywhere, there are going to be quirks between platforms. You should be running the same stack as your production environment. And most importantly you should be running the same stack as me.

Why? Well, let’s say that for some strange reason you want to follow this guide and code along with me. I’m going to say “now do XYZ”, and then you’re gonna write a comment like “But how do I XYZ on a Mac?” and then I will have to take out my MacBook, and fucking wait for homebrew to compile twenty seven things. And then all I’m gonna find out is that you are bugged more than me because you’re running OSX Snoop Lion, whereas I’m already on OSX Space Leopard and one of us would totally need to up-down-grade or something.

So we are gonna bypass that. If you are not sitting on a LAMP right now, go fucking download Vagrant. In fact, download it anyway and we will set it up to keep your PHP shit away from your normal shit.

Setting Up a Sweet-Ass Development Environment

I should probably explain Vagrant here, though we are going to get side tracked by this. Vagrant will let you set up a virtual machine in about 5 minutes. It lets you go from nothing, to a full fledged virtual linux box running inside your computer in minutes, with no installers, no fucking around, and no 800GB image downloads.

Here is what you do. First, pick a folder somewhere on your system where you want the files that you share between the VM (say ~/vagrant, or maybe %USERPROFILE%\Documents\vagrant if on Widnows) then open a terminal:

mkdir ~/vagrant
cd ~/vagrant
vagrant box add base http://files.vagrantup.com/lucid32.box
vagrant init
vagrant up
vagrant ssh

If all went well, you should now be sitting on a freshly installed linux box prompt. Yeah, it is that easy… Unless you are on Windows, where you actually have to futz around with PuTTY like an idiot for another five minutes. It pretty much goes like this:

  1. Run PuTTYGen
  2. Load %USERPROFILE%\.vagrant.d\insecure_private_key
  3. Save private key as idontevencare.ppk
  4. Pull up Putty, use localhost and port 2222
  5. On the sidebar go to SSH->Auth and load the file you just created
  6. Save the profile so you don’t have to do it again and hit connect
  7. Log in as vagrant

Now you have the L part of the equation, and it is time to install the AMP part. You can actually get pretty fancy with Vagrant – it supports provisioning with puppet and cheff but we don’t need that. We’ll just do it the old-fashioned way:

sudo aptitude update
sudo aptitude install php5 php-mysql apache2 mysql-server

Now we are all set. All is left is to make our freshly minted server accessible from your host system. So head back to that folder you picked to dump your Vagrant shit into and open the Vagrantfile that was created there and un-comment the following line:

config.vm.forward_port 80, 8080

This will let you access the virtual server by hitting http://localhost:8080. If you’re following along, you can hit that link to see if it works. It shouldn’t, because the config file change won’t take effect until you restart your VM with the following command:

vagrant reload

Here is a bonus trick: move apache document root from /var/www to /vagrant/whatever. Why? Because the /vagrant directory is shared between the VM and host. This means you can edit the file directly on your host OS, using your favorite editor or IDE.

Now we all have a nice LAMP server, let’s move on to actual PHP stuff. I’m already close to a thousand words in, and I haven’t even started getting on topic. Hey guys, I have an idea – I’m gonna start a series of posts about PHP and then talk about everything in the fucking word rather than this damnable language!

Tools of the Trade

At the pace this is going we’re definitely not writing any actual code today, so let’s talk some more about setup. Here is what I want to use for this project:

But Luke, all of this for a pastebin?

Shut your mouth, you knew what this was. I’m doing a thing where we are going to pull in all these tools knowing full well its an overkill for the sole purpose of learning about them.

The first two items on your list are development time dependencies, so I suggest installing them globally.

PHPUnit and PHPDocumentor

Why did I choose PHPUnit and PHPDocumentor? There are many other popular projects out there – like SimpleTest for example. So why these two? Because I said so. Also, these two projects are more or less the de-facto industry standards. They are ubiquitous and by far the most popular choices. So if you are going to be doing anything with PHP, you will see them crop up sooner or later.

We could install them on a per-project basis, but I find that it is best to just bite the bullet and load them system wide, and save you some hassle later. To do that we will need pear which is old and uncool package manager for PHP projects. It’s not very much liked, because unlike equivalent package managers for other languages (like gem for Ruby and npm for Node.js) you can’t just register your project with it. You have to be a cool dude, or do sexual favors for the project founders or something like that.

Anyway, let’s install pear the usual way:

sudo aptitude install php-pear

Now let’s download PHPUnit:

sudo pear channel-update pear.php.net
sudo pear upgrade-all
sudo pear config-set auto_discover 1
sudo pear install pear.phpunit.de/PHPUnit

Yeah, did I mention the interface on that thing is totally great? I honestly don’t even want to know what is the deal with these channels and auto discoveries – the whole thing is bizarre and byzantine. Also buggy.

You will likely encounter this error because I did:

Duplicate package channel://pear.phpunit.de/File_Iterator-1.3.3 found
Duplicate package channel://pear.phpunit.de/File_Iterator-1.3.2 found
install failed

Here is the solution:

sudo pear clear-cache
sudo pear install phpunit/File_Iterator
sudo pear install phpunit/Text_Template
sudo pear install --force --alldeps pear.phpunit.de/PHPUnit

This should now get you a semi-working version. If it is still bugging out when trying to run tests, do this:

sudo pear channel-discover components.ez.no
sudo pear install phpunit/phpcpd

sudo pear install --force phpunit/PHP_File_Iterator-1.3.2
sudo pear install --force phpunit/PHP_Timer-1.0.3
sudo pear install --force phpunit/Text_Template-1.1.2
sudo pear install --force phpunit/PHP_CodeCoverage-1.1.2
sudo pear install --force phpunit/PHP_TokenStream-1.1.2

I’d explain, but honestly – I don’t actually give a flying fuck what happened there. The less I need to use pear, the more sanity I retain and therefore I will refrain from delving into this issue. Suffice to say this quick fix worked for me.

If you are interested (or a glutton for punishment) here is the rundown of the situation: for some odd reason PHPUnit 3.6 has wrong dependencies marked in the Pear package system. When you install PHPUnit via Pear, it looks at you PHP version and if you have 5.3.3 it installs the latest version which is 3.7. If you don’t, it installs 3.6. Some of the dependencies broke backwards compatibility with 3.6 some time ago so logically Pear should download the latest compatible versions. But for the above-listed packages it downloads latest and greatest which causes PHPUnit 3.6 fail in unpredictably fun ways. Or in other words, pear is shit package manager.

PHPDocumentor can be installed without any major advetures:

sudo pear channel-discover pear.phpdoc.org
sudo pear install phpdoc/phpDocumentor-alpha

The nice thing about having installed these things globally is that you now have them in your path, available as nifty commands phpunit and phpdoc respectively. I will talk about these in more detail when we actually get to use them (which may actually happen in the next eternity or so).

Composer: managing run-time dependencies

Let’s talk about run time dependencies. I mentioned we will be using Twig and RedBean which are a templating framework and an ORM respectively. I could try installing these globally, but they are really project specific bundles of code. Next project you will be working on might require you to use Doctrine or Propel for example. And then what? You’re going to have all these different ORM frameworks crufting up your dev-space.

Granted, you could always roll a new Vagrant server for the next project and start clean, but… Well, PHP community came up with a solution for this particular problem – a run time dependency manager named Composer. It is the new and hot thing as compared to the old and busted Pear.

How is Composer different from Pear, Gem, Pip or Npm tools? It manages dependencies on a project level, not at a system level. You tell it what packages you need for this specific project, and it installs them into the project folder, already bundled up and ready for deployment. How do you install this wonder?

cd /tmp
curl -s https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer

That’s it – from this point on, it will be available system wide using the composer command.

You may want to wrap this up

Quick note before we start plowing ahead. This is a good spot to wrap up your vagrant machine into a box. Vagrant lets you save your current VM into a file that you can deploy somewhere else later. That’s sort of where it got its name from – you can build a machine once and then distribute it to all your buddies yielding an identical development environment for all of you, regardless of what OS and software each person is actually running.

To save the state, you just issue a quick command (on the host OS):

vagrant package

This will put a package.box file in ~/vagrant/ or wherever your Vagrantfile lives. This box contains the entire environment the exact way you set it up. For us, this would mean a complete LAMP stack, PHPUnit, PHPDocumentor and Composer all set up and ready to go. So let’s save this file for later in case we will want to start a new project from scratch at some point in the near future.

When the day you are ready to tear down your current vagrant and start over comes, just do this:

mkdir ~/newvagrant
cd ~/newvagrant
vagrant box add lamp /path/to/package.box
vagrant init

Then edit your ~/newvagrant/Vagrantfile to be:

Vagrant::Config.run do |config|
  config.vm.box = "lamp"
  config.vm.forward_port 80, 8080
end

Then just boot it up it with vagrant up and you should be ready to go.

Twig and RedBean

Why are we using these things again? Well, I guess the best way to explain this is to give you an example. Twig is a feature-full, but easy to use templating engine with neat features such as auto-escaping (in case you forget), and RedBean is a completely hands-off ORM that almost completely removes the need for writing SQL statements by hand. Combined together, they allow you to write code that is incredibly succinct, clean and at the same time relatively safe.

For example, you could define a template file (let’s call it paste.html) like this:

{% include header.html %}
  

PASTE #{{ id }}

{{ content }} new paste {% include footer.html %}

Then somewhere else in our code we could render it like this:

// fetch paste from the db
$paste = R::load('paste', $id);
// render it using paste.html
echo $twig->render('paste.twig', array('id' => paste->id, 'content' => paste->content));

This is of course an oversimplified pseudo-code. There is a little bit more setup but you can easily see the benefits here. Your HTML lives separately from PHP code in files that cannot be executed by themselves. What you probably don’t see in this this bit of code is the escaping I was harping about so much last week. That’s because it’s not necessary. Twig can be configured to auto-escape all template variables by default. RedBean automatically sanitizes all the input you pas into it. It’s pretty much auto-magical.

Let’s get down to it

This post is getting a little bit long, but I want to accomplish one more thing today – setting up your project. This way we will be ready to go to actually throw down some code in next installment.

So let’s visit our project folder and set a few things up:

cd /vagrant/www
git init
touch composer.json
touch .gitignore

I probably should not say this, but git init should be the absolute first command you issue when embarking on any kind of creative project. I said this before, but it bears repeating over and over and over again until it becomes your mantra:

  • if time spent making it >= 5min, put it under version control

Fortunately for us, PHP does not generate that many cruft files we would need to exclude from our repository. So our .gitignore is only going to have a single line:

vendor/

What is this directory? That’s where Composer is going to dump all the dependencies. You don’t want to commit those things with your project – it is a waste of space and time. If you publish your code later, contributors will be able to automatically fetch these dependencies with Composer. That’s one of the reasons we are using it. So lets set it up.

Inside composer.json we put references to Twig and RedBean like this:

{
    "require": {
        "twig/twig": "1.*",
        "gabordemooij/redbean": "dev-master"
    }
}

Make sure you don’t eat up any brackets or commas – the file needs to be a valid json for the magic to work.

Finally, let’s allow composer to do it’s work:

composer install

You should now see new files and folders magically appearing in your folder:

  • The vendor/ directory contains all the files necessary to use Twig and RedBean
  • The composer.lock file “locks” the project to current versions the dependencies. You will want to commit it to your version control and publish it with your project code. This way, someone who downloads your code will get the exact same versions of Twig and RedBean as you (eg. same commit for RedBean and same minor version for Twig).

All that is left to do now, is to git it all up:

git add .
git commit -m "First commit"

And there you have it. We are now ready to go, we all have uniform development environment and we are all ready to write some code. Which will happen next time… Maybe.

One more thing…

I almost forgot – if you want to shut down your vagrant instance you have two options:

  1. vagrant suspend will “hibernate” your VM to disk. You can then vagrant resume with about a 10 sec boot time. The only downside is that it will use extra disk space to save current state but this should not be an issue on most machines.
  2. vagrant halt will perform a graceful shut down. You will have to use vagrant up to power it up again. You probably want to do this if your shit is all cray, and you want to do a clean reboot.

I’m hoping this is helpful and/or entertaining because I have at least one or two more of these in me (depending on how many times I get side tracked and go on a random tangent).

]]>
http://www.terminally-incoherent.com/blog/2012/12/19/php-like-a-pro-part-2/feed/ 4 PHP Like a Pro: Part 1 http://www.terminally-incoherent.com/blog/2012/12/17/php-like-a-pro-part-1/ http://www.terminally-incoherent.com/blog/2012/12/17/php-like-a-pro-part-1/#comments Mon, 17 Dec 2012 15:07:56 +0000 http://www.terminally-incoherent.com/blog/?p=13267 Continue reading ]]> PHP gets a lot of flack for being a shitty language. This reputation is unfortunately well deserved. It is an ugly, quirky, idiosyncratic mess. But at the same time, PHP remains one of the most popular web programming languages out in the wild. Why? Because it is easy. Easy to install, easy to learn, easy to get working pages starting from scratch. The learning curve is gentle and the time interval it takes to go from a blank editor window, to actual dynamically generated page is extremely short.

But as most things that are easy to learn, PHP tends to be difficult to master. While you can start building functional pages right away, you likely won’t be winning any awards for nicely structured code. PHP lends itself to disturbingly messy, ugly and insecure implementations.

So, seeing how we are stuck with it, how do we make PHP suck less? Well, lets talk about this. Let’s try to sit down and establish how would we go about starting a new PHP based project in such a way that it’s future maintainer is not going to want to strangle you in your sleep. Ideally we would like to create something that a random code monkey can pick up and go “Well… I don’t completely hate this…”

Why not go for “I like”? Because that just does not happen. Firstly, we’re talking about PHP here. Secondly, no one ever likes someone else’s code. Ever. Someone could give me the cleanest, most expressive and brilliant piece of code in existence, and there is a high chance I would be like “Fuck this guy and fuck his bullshit K&R indenting!”

Don’t try this at home!

First, let me show you what can, but shouldn’t be done with PHP. Let’s take a very simplistic application and build it the quick and dirty way. What is the simplest web app you can think of? For me it’s a pastebin – just a simple page with a text box hooked up to a database with a single table that only has two columns: id and content. It is something you can implement in about 15 minutes, and 15 lines of code.

No seriously, here is a functional pastebin app in 15 lines of terrible, terrible, ugly and buggy PHP:

Paste It
          

"; } else { $mysqli->query("insert into pastebin (content) values ('$_POST[content]')"); echo "

Thanks

Your paste is here: insert_id>PASTE #$mysqli->insert_id

"; } } else { $tmp = mysqli_fetch_assoc($mysqli->query("select * from pastebin where id=$_GET[paste]")); echo "

PASTE #$tmp[id]

$tmp[content]new paste"; } ?>

This code actually works. It is small. It fits on the screen, and it fits in your head which is actually a good thing. Code that doesn’t fit on a screen, tends not to fit in your head which makes it difficult to debug it. This code is succinct and to the point.

So what exactly is wrong with it? Well, where do I start? Actually, I know – let’s start with the most glaring mistake also known as the Bobby Tables problem:

$mysqli->query("insert into pastebin (content) values ('$_POST[content]')");

If this code would ever touch live internet, the effects could be disastrous as it is prime target for SQL injection. Now I might have had enough forethought not to give the testuser privileges to drop tables, but it is likely can insert, update and delete at will. Few lines below this, I take the same un-escaped user provided string and echo it to the page opening myself up to a XSS attack. If an industrious user would drop a properly formatted iframe in there he could serve just about any code from my page creating all kinds of mischief.

Then of course there is a whole litany of less glaring issues that would nevertheless induce murderous rage in most programmers. For example, there is no error checking or input validation in there whatsoever. So if anything was go go wrong, the entire app will explode in spectacular ways. There is really no robust way to test for problems either. Since it’s only 15 lines refactoring this code shouldn’t be a huge issue – but imagine few hundred lines written in this style – weaving a thick, convoluted tapestry of overlapping PHP, HTML and SQL. How do you keep it all in order? You really can’t.

Granted, this code is this terrible because I was golfing – i was trying to write my app in as few lines of code as possible so that it fits neatly in a little code box here on the blog. But, you can find code of this quality out there in the wild… And some of it is probably mine. Or yours.

What should we do then?

How should you write PHP code? Well, not like that. Just don’t do what I did just now and you should be fine. Solid advice, eh? But I think I can do a little better than this. So here are some guidelines that are probably going to help you make things in PHP that don’t suck as much as my pastebin example.

Follow the Standards

I think everyone agrees that the rule #1 of php (and programming in general) is “don’t write shitty code”. Or at least that’s one of the many #1 rules of programming. I can’t really keep track of all of them, but this is definitely an important one. The problem with this rule is that everyone has their own definition of what can be considered a “shitty code”. Please refer to the “Fuck this guy and fuck his bullshit K&R indenting!” commentary just few paragraphs above to see that this is true.

Hence we invented coding standards. Coding standards are rules and guidelines that everyone hates, but agrees to use for the sake of consistency. They are usually chosen in such a way as to offend every programmer equally regardless of their favorite variable naming conventions, indent styles or coding habits.

As it is to be expected with PHP, it has about six billion conflicting standards. Zend has one, Pear has one, Horde has one. Which one should you adhere to? Good question. I think the best bet is to go with the standards published by the PHP Interop group cryptically named:

I’m fairly sure these numbers are supposed to represent amount of fucks you give about interoperability of your code. So if you don’t give a fuck, you should at the very least try to conform to PSR-0, unless you enjoy reading issues like “y u no psr?” in your issue tracker on Github. If you actually give a fuck, then you should read PSR-1. Or if you really want to be an awesome dude, and you give all the fucks, you should conform to PSR-2.

All of these are mostly common sense stuff, and you probably won’t piss you off much. It also won’t prevent you from writing terribly shitty code, but it may help avoiding common mistakes just by forcing you to drop (or prevent you from developing) harmful habits.

Orient yourself Objectively

OOP is great. It also sucks. You take the good with the bad, but most of the time it will help. At the very least it will force you to organize your code. If I thought about “pastes” as objects, that can be initialized with some text and then stored in the database I could have abstracted all that logic into a separate class file keeping it separate from the display layer available to the user.

Once you start encapsulating logic into methods, it puts you in a completely different fame of mind. In my purely procedural quickie code above, I was mainly concerned with stuff flying into and out of the database with my code being more or less the revolving door in between. Re-thinking that code as in OOP terms would make me more likely to actually add some validation and error checking code in there, because that’s just what you do.

Not to mention the class could be isolated and tested separately from the interface.

Unit Testing

My pastebin is a little bit simplistic example for this. But imagine building something a tad bigger – like a blog, or a shopping cart. Chances are you will be organizing our code into neatly encapsulated classes. But you can’t write these blind and just hope things will work. So in most cases you will be building little dirty hackish test mock-ups with a lot of echo statements to make sure your methods are working properly, that properties are accessible, that you are polymorphing your stuff adequatly and etc…

What you might not realize is that you are essentially unit testing your code this way. You are just doing a really terrible job at it, and your coverage blows, but it still counts. What if hitting the reload button on the browser after every change in code you could just fire up a test from the command line and just watch it beat your code up?

So instead spending time half-assing custom made test pages, you ought to just bite the bullet and write formal unit tests. Might as well do it correctly, no? Here is what no one tells young programmers about unit tests: they are not there to detect errors. At least not at first. At first they won’t detect shit, because you will write a test, and then will write the code which will pass said test. Which makes it seem like Unit Tests are just needless busy work. But the secret is that they make you start thinking about edge cases, and oddball behaviors in your code. If you didn’t have to put something in that unit test class, chances are you would just test the 2-3 most common inputs and then fuck-off to do the next thing on your check-list. So the initial benefit is that they help you focus your mind on thins you would likely overlook otherwise.

The error detection thing comes in handy later, when you go back, change bunch of things in your code and watch the carefully constructed tests fail because you of course forgot to sanitize some input, or some other stupid thing.

Document everything

There is no such thing as self-documenting code. No matter how rigorous and clean your coding standards are, it could always benefit from some comments. And by comments I mean real comments, not pseudo code or redundant crap like this:

// loop through array and display each item
foreach ($array as $value) {
    echo $value;
}

Really? You don’t say? Shit, if you didn’t comment that I wouldn’t have known. How about you tell me why you are doing this. I can see what the code is doing, but it always helps to know why it is doing it. What’s the purpose? Where is it going to be used? Of course I could read your code, and jump from method to method until I can actually have a clear picture of what is going on in my mind, but if you tell me, I don’t have to do any of that.

You know what is also nice? That JavaDoc thing that Java has. You know, those neatly defined comment blocks that describe each class and method, that can then be slurped out of your code and puked out into HTML to create a nifty user guide and API reference? That shit is great, because it actually makes people comment things in the correct way. You know that your blurb will become part of a web style manual that someone is supposed to be able to read in order to learn how to properly use your class/method without actually looking at the implementation, and that’s how you write it. You describe the behavior without doing into detail about implementation.

You should be doing this for all your PHP classes. And yes, there is a way to create those nice HTML docs too.

Do the MVC

For most web projects the Model-View-Controller pattern just makes sense. Even my pastebin example, even if simplistic could use some clear separation between the code that handles the database CRUD operations, and the display layer that the user interacts with.

Keep in mind I’m not saying you need to get married to a full blown MVC framework. Marriage is a beautiful thing, but you probably shouldn’t make such a commitment unless you are absolutely sure this is what you want. CakePHP might be great for a medium sized project but I really think it would be an overkill to use it for my pastebin. There are cases where it is probably a good idea to roll your own MVC like pattern, or choose something really lightweight to do it for you.

Always Escape!

Don’t trust the users. No, seriously. I can’t emphasize this enough. Shit they put into text boxes should never, ever touch the back end without being beaten into submission and sanitized. Most programmers know not to do stuff like this:

echo "Looking up record with id: " . $_GET["id"]; // XSS FAIL
$result = $mysql->query("SELECT * FROM sometable WHERE id='$_GET[id]'"); // SQL Injection FAIL

Unfortunately, sometimes you won’t use the user provided data until much later in your code. Sometimes you get it, stuff it into a an innocent looking variable like $foo, then let it percolate throughout your code. Many screen-fulls later you finally get to the point where you are about to store $foo in the database, completely forgetting it contains tainted, unsafe and potentially destructive payload.

Joel Spolsky actually wrote a long-ass article about this a while ago. There are ways you can actually force yourself to remember these sort of things. How? Hungarian notation is one of such methods.

Wait! Stop! Sit your ass back down! I’m not talking about the brain-dead version where you use prefixes to hint at data types… Which actually is not a terrible idea in a dynamically typed language like PHP. But that’s not what I mean. I mean the Spolsky brand of sane Hunarian where you use prefixes to differentiate between different kids of data (not types, kids) – like safe and unsafe strings for example.

When building crap that will face the internet, I like to prefix my variables with U if they contain Un-escaped, Unsafe, Unseemly User generated data. Conversely I use S for Safe, Sanitized data ready to be Shown or Stored in the database. So for example:

$uName = $_POST["name"]; // unsafe string
$sName = Sanitize($uName); // now safe via some magic function

Why does it work? Because eventually you train yourself to catch logical errors as if they were syntax errors. For example, seeing a U variable anywhere near database related code should make you very uneasy.

And yeah, I know the PSR guidelines say that Hungarian notation should burn in hell, and that you shouldn’t put visibility or type information in the variable names… But, I think in this case we might be justified – we’re not indicating type after all, but rather important piece of logic here.

Don’t make a soup out of your code

I mentioned this before – my pastebin example is like a bad soup – an unholy mix of ingredients that probably should not co-exist in a single document: PHP, HTML and SQL. In a perfect world, your HTML should reside in their own little universe away from dynamic code.

There is just something dirty and nu-sophisticated about using print statements to assemble HTML code. It should not be done that way. Ideally you would want your HTML to be made by a designer. You know, one of those nice people who don’t know shit about coding but have kick-ass skills in Photoshop, and can actually wizard up a page that doesn’t look like shit in IE6 without committing a suicide. Those guys like to work with pure HTML templates, that merely have little place-holder text and “holes” where the dynamically generated content will be added later.

Even if you don’t have a friendly designer prettifying your pages for you, HTML and CSS is much easier to work with and maintain if it’s not churned out by print statements, or interleaved with finicky business logic. That template/placeholder thing is a really good idea.

So, why not make it a thing and use a templating engine of some sort. This way you separate your client side, and server side code almost completely making your life much easier.

Similarly, you really don’t want to be embedding a lot of SQL in your PHP. It is prone to errors, can be exploited way to easily, and it is not straightforward to test. Ideally, your code should manipulate objects, which then in turn ought to transparently save their data to the database.

Don’t get me wrong, I am actually one of those weirdos that actually likes writing SQL statements. I mean, I wrote half a fucking thesis on blind database integration. It is very likely that I have forgotten of algebra that many of you even knew existed. Can I write a better query than some shmuck who threw together this or that ORM? Probably… Well, maybe…

Actually, no. If it is a sizable ORM with a decent sized community chances are it was pretty robustly tested. There are many eyes on that code so to speak whereas I am very likely to just go “fuck it, this worked for the two cases I just tried, therefore I pronounce it bug free and bulletproof.” Not to mention, anyone reading may queries may actually loose sanity points just for trying to follow my “optimization” attempts. So, even if you know your S, your Q and your L very well, sometimes it might be safer and more kosher to abstract this stuff onto some ORM engine.

Would I need an enterprise level ORM for my pastebin example? No, definitely not. In that case I would probably just use PDO instead of the native driver and call it a day. PDO has built in parametrized query support which would help me with those nasty SQL injection issues. On the other hand pastebins actually come in all shapes and sizes. If I wanted to make it extensible, I could probably make a case for picking a robust ORM to preserve sanity and make code more maintainer friendly.

Manage Dependencies

If you were keeping track, I just mentioned a whole bunch of third party code you might or might not be pulling into your project. Let’s count them: a unit testing framework, a documentation builder, a templating engine, an ORM, maybe some more stuff. Is this smart? Is this a good practice?

Well, it depends. In general, you don’t want to re-invent the wheel every time you start a new project. The more code you have to write, the more bugs you are going to introduce. So by using other people’s code you limit the attack surface of your own code, and also decreasing the amount of code you have to keep in mind at any given time. So it makes sense.

But if you will be pulling in a lot of other people’s code, you ought to have some way of managing these dependencies. Ideally, you would want a tool that would let you automatically download and update and configure all the things you need before you even write a single line of code.

A Modest Proposal

Here is a crazy idea: how about we take this pitiful pastebin code and re-design it, doing it right. And by right, I mean going completely overboard. We are going to use an MVC like approach, and implement it using a full blown templating engine, ORM engine, we will write formal unit tests for everything that can be tested and we will automatically manage run time and development time dependencies. Yes, it is a complete overkill but I guess that’s sort of the point.

This project is small enough to be able to do this in just a few short posts, and the code should be succinct enough to post here almost in its entirety. I should be able to illustrate the tools and concepts much better suited to bigger projects, using something very simple and easy to grasp.

In part 2 I’m going to discuss the tools/frameworks/engines I picked for this project, and show you how to set them up so that they can be managed almost without any conscious effort.

]]>
http://www.terminally-incoherent.com/blog/2012/12/17/php-like-a-pro-part-1/feed/ 6