Lesson Monday

Lesson goals:

  • Build a web app using Silex
  • Learn to define a route with an anonymous function in a Silex app

What are we doing here and why

It's time to build our first web app. We'll use Silex, a micro framework which will allow us to create web apps quickly. Let's recreate that little online greeting card we wrote to our friends in the "Variables make life easier" lesson.

Setup project folder

First, we need to create a project folder. Name it anything you like, but inside of it you will need two folders with exactly these names: app, and web. For this example, we'll call it greeting.

Now, open a second Terminal window, change directory into the web folder you created, and start the PHP server with the usual php -S localhost:8000 command. This makes the web folder into your document root. We will refer to the top level folder holding app and web as your project folder.

Create composer file and get Silex dependencies.

Next, we need to use Composer to get the Silex files. Create the composer.json file in your project folder and copy this text into it:

composer.json
{
    "require": {
        "silex/silex": "~1.1"
    }
}

Now Composer will know that Silex is a dependency of your project. Next, in a new Terminal window, change directory into the project folder and run the install command: > composer install

Next, create the setup file: app.php

Then create a file called app.php inside of the "app" folder and type this into it:

NOTE: If you should encounter timezone errors, please include this code at the top of your app.php, inside the <?php opening tag:

date_default_timezone_set('America/Los_Angeles');

app/app.php
<?php
    date_default_timezone_set('America/Los_Angeles');
    require_once __DIR__."/../vendor/autoload.php";

    $app = new Silex\Application();

    $app->get("/hello", function() {
        return "Hello friend!";
    });

    return $app;
?>

create index.php

Next, inside of your web folder, create the file "index.php" and type this into it:

web/index.php
<?php
    $website = require_once __DIR__.'/../app/app.php';
    $website->run();
?>

Now, we can finally go to localhost:8000/hello in our web browser and see it say "Hello friend!"

Let's go through this code one line at a time, starting with the app.php file.

require_once:

The first line in this file uses a new keyword called require_once. It opens the autoload.php file which Composer just put into the vendor folder. This file is responsible for automatically loading all the other Silex files in the vendor folder into our project. Any time that you need to include external files in your code, you'll use the require_once keyword, followed by the path to the file. You need to include the path in double quotes and add a semicolon at the end of the line.

The keyword DIR

The path to the file is constructed by first using the keyword __DIR__, which refers to your document root (the "web" folder). Then the . attaches the rest of the path to the document root path, making it into one string. The /../ means "go up one level in the folder hierarchy". So this path means start in the "web" folder, then go up to the project folder, then the vendor/autoload.php part says to go into the "vendor" folder, find the autoloader.php file, and open it.

Instantiate the application object

The next line of code $app = new Silex\Application(); creates a new instance of the class Silex\Application(), and stores it in a new variable called $app. This object represents our entire website as an entity. We instantiate it just like we instantiated our own classes of Car and Contact. The only difference is that someone else wrote the Silex\Application class and it has a lot more properties and methods than any classes we have made - so its declaration is stored in the external files we loaded using the require_once keyword.

Create a route using the application object

The next chunk of code defines a route. A route is similar to a route on a map. It shows the PHP interpreter where to go and what code to execute when a particular address is typed into the browser.

We use the familiar object operator to call the get method on the $app object we just created. This method takes a string as its first argument - in this case "/hello". This string refers to the URL path for a page we want to create on our website. When the URL for the page is visited, the function that is the second argument is run. In this case, when you type localhost:8000/hello into the browser, our function simply returns the words "Hello friend!" to be displayed on the page.

We define the function in that second argument almost the same way we're used to, but this time we don't need to give it a name, since it isn't used anywhere else in our code. We call this an anonymous function.

Return the application object and create the index page which continues to use it.

Finally, in the last line of code the object $app is returned. Now wait a minute, what are we returning from? This is not a function or a class is it? Well, it will make sense when you look at what's in the other file: index.php.

In the first line, again we are using require_once. But this time we are calling the app.php file that we just made! And when app.php returns the application object at the end of its code, that object is stored in a variable called $website in index.php. Then we use that object to start our web app by calling its run method.

Let's add another route to a page which says "Goodbye friend!". Open your app.php file and add the following code, right above the last line returning the application object:

app/app.php
...
$app->get("/goodbye", function() {
    return "Goodbye friend!";
});

return $app;

Now, we can go to localhost:8000/goodbye and the browser will say "Goodbye friend!".

Suppose we want the /hello page to be a 'home page' for our website. We don't want our users to have to type /hello at the end of our website's URL to get to the homepage: they should just get there whenever they type the URL as it is. In this case, we would just modify the /hello route to look for only a /. This is called the root path or base path - meaning there is no URL path to follow beyond the single slash.

app/app.php
$app->get("/", function() {
    return "Hello friend!";
});

Now you can go to localhost:8000 and still see "Hello friend!".

Sweet! Let's modify the root path to include some HTML from our greeting card.

app/app.php
    $app->get("/", function() {
        return
        "<!DOCTYPE html>
        <html>
        <head>
            <title>Hello Friend!</title>
            <link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css'>
        </head>
        <body>
            <div class='container'>
                <h1>Hello From Afar</h1>
                <p>Dear Friend,</p>
                <p>How are you? I hope that you are having a nice weekend. I'm vacationing in the mountains of Tibet while I learn programming! </p>
                <p>Friend, you would not believe how cold it is here. I should have gone to Hawaii instead.</p> 
                <p>But I like programming a lot, so I've got that going for me. </p>
                <p>Looking forward to seeing you soon. I'll bring you back a souvenir. </p>
                <p>Cheers,</p>
                <p>Travel Enthusiast Jane</p>
            </div>
        </body>
        </html>"
        ;
    });

One last detail. No one navigates websites by typing the URL in for every page they want to visit - usually you can click on links to do that. Let's add a link to our homepage which would take the user to the /goodbye page. Add this line in at the bottom of the letter, right above the closing </div> tag under the <p>Travel Enthusiast Jane</p>.

app/app.php
<a href='/goodbye'>Goodbye!</a>

The href attribute points to the URL defined in the /goodbye route. Now if you reload the page, you should be able to click on the "Goodbye!" link to see the /goodbye page.

Here is a summary of the basic Silex setup steps:

  • Create project folder with subfolders:
/project_name/app 
/project_name/web
  • Start server in web folder: php -S localhost:8000

  • Create composer.json and run composer install in the top level of your project folder

composer.json
{
    "require": {
        "silex/silex": "~1.1"
    }
}
  • In your app folder, create app.php.

  • Here is a good starting point for app.php. It loads the $app object, creates a basic route at the root path /, and then returns the $app object.

app/app.php
<?php
    require_once __DIR__."/../vendor/autoload.php";

    $app = new Silex\Application();

    $app->get("/", function() {
        return "Hi there!";
    });

    return $app;
?>
  • Inside of the web folder, create the file "index.php":
web/index.php
<?php
    $website = require_once __DIR__.'/../app/app.php';
    $website->run();
?>
  • Now you can go look at your first route by going to http://localhost:8000