Home Symphony Create your own framework based on Symfony2.(Part 3)

Create your own framework based on Symfony2.(Part 3)

by admin

Table of contents

So far, our application has been quite simple, since it contains only one page.
To complicate things a bit, we add another page saying "goodbye":

<?php// framework/bye.phprequire_once __DIR__.'/autoload.php';use SymfonyComponentHttpFoundationRequest;use SymfonyComponentHttpFoundationResponse;$request= Request::createFromGlobals();$response= new Response('Goodbye!');$response-> send();

As you can see, most of the code is exactly the same as for the first page.
Let’s highlight the common code for all our pages separately.
"Highlighting common code" sounds like a great plan for our first "real" framework!
Within PHP, refactoring will consist of creating a plugin file :

<?php// framework/init.phprequire_once __DIR__.'/autoload.php';use SymfonyComponentHttpFoundationRequest;use SymfonyComponentHttpFoundationResponse;$request = Request::createFromGlobals();$response = new Response();

Let’s see it in action :

<?php// framework/index.phprequire_once __DIR__.'/init.php';$input = $request-> get('name', 'World');$response-> setContent(sprintf('Hello %s', htmlspecialchars($input, ENT_QUOTES, 'UTF-8')));$response-> send();

And for page " Goodbye ":

<?php// framework/bye.phprequire_once __DIR__.'/init.php';$response-> setContent('Goodbye!');$response-> send();

So, we have collected the general code in one place.But that doesn’t look like a good level of abstraction, does it?
First, we still have the method call send() on all pages, so our pages don’t look like normal templates, and we still can’t "properly" test the code.
Another serious problem is that adding a new page means that we have to create a new PHP script,
whose name will be passed to the URL (http://example.com/bye.php) and will be called by the webserver.
It’s a good idea to move the script call to our code for more flexibility.
This can be achieved by redirecting all requests to one PHP script, the front controller.

The script itself might look like this :

<?php// framework/front.phprequire_once __DIR__.'/autoload.php';use SymfonyComponentHttpFoundationRequest;use SymfonyComponentHttpFoundationResponse;$request = Request::createFromGlobals();$response = new Response();$map= array('/hello' => __DIR__.'/hello.php', '/bye' => __DIR__.'/bye.php', );$path = $request-> getPathInfo();if (isset($map[$path])) {require $map[$path];} else {$response-> setStatusCode(404);$response-> setContent('Not Found');}$response-> send();

And an example of a new hello.php :

<?php// framework/hello.php$input = $request-> get('name', 'World');$response-> setContent(sprintf('Hello %s', htmlspecialchars($input, ENT_QUOTES, 'UTF-8')));

In the script front.php , $map Maps the paths in the URLs to the corresponding paths of the script files.
As a bonus, if the client asks for a path that is not defined in the matching array, we return a 404 error.
Now you control the site yourself.
Now you must use the front controller’s script to access the pages front.php :

/hello and /bye are the "paths" of the pages.

Most web servers, such as Apache or nginx, are capable of rewriting
requested URLs of pages, thereby removing the front controller’s file name ( front.php ),
So your users will just have to type example.com/hello?name=Fabien , which looks considerably better.

So, the trick is to use the method Request::getPathInfo() which returns part of the path from " Request "
removing from it the front controller’s file name including all subdirectories (only if needed – see hint above).

You don’t even need to set up a web server. Just
replace the call to $request = Request::createFromGlobals(); to something like $request = Request::create(‘/hello?name=Fabien’); ,
where the argument is the query you want to simulate.

Now that all requests to our pages go through one script (front.php),
we can contribute to the security by moving all the other PHP files outside the root web directory :

 example.com+-- composer.json¦ src¦ +-- autoload.php¦ L-- pages¦ +-- hello.php¦ L-- bye.php+-- vendorL-- webL-- front.php 

Now, configure your web server so that the directory web/ Is the root directory for it.
That’s it, the other files are no longer available to the client.

For this directory structure to work, you will need to change the paths in some project files;
these changes will be homework for the reader.

The last thing that is repeated on every page is the call setContent()
Now we can turn all our pages into "templates" simply by making the content output
and calling setContent() directly from the front controller script :

<?php// example.com/web/front.php// ...$path = $request-> getPathInfo();if (isset($map[$path])) {ob_start();include $map[$path];$response-> setContent(ob_get_clean());} else {$response-> setStatusCode(404);$response-> setContent('Not Found');}// ...

Now the script hello.php can be converted into a template :

<!-- example.com/src/pages/hello.php --><?php $name = $request-> get('name', 'World') ?>Hello <?php echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?>

As of today, our framework looks like this :

<?php// example.com/web/front.phprequire_once __DIR__.'/../src/autoload.php';use SymfonyComponentHttpFoundationRequest;use SymfonyComponentHttpFoundationResponse;$request = Request::createFromGlobals();$response = new Response();$map = array('/hello' => __DIR__.'/../src/pages/hello.php', '/bye' => __DIR__.'/../src/pages/bye.php', );$path = $request-> getPathInfo();if (isset($map[$path])) {ob_start();include $map[$path];$response-> setContent(ob_get_clean());} else {$response-> setStatusCode(404);$response-> setContent('Not Found');}$response-> send();

Adding pages is done in two steps: add element to the array of matches and create
PHP template in src/pages/ From the template we can access the data " Request " through the variable $request
and we can manipulate the response headers(" Response ") through the variable $response

If you decide to stop now, it’s a good idea to put an array of URL matches in the configuration file.

You may also like