📜 ⬆️ ⬇️

We create our own framework based on symfony2. (Part 3)

Table of contents





Until now, our application was quite simple, because it contained only one page.
To make things a bit more complicated, let's add another page saying “goodbye”:
<?php // framework/bye.php require_once __DIR__.'/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; $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 separate the common code for all of our pages separately.
“Selecting a 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.php require_once __DIR__.'/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; $request = Request::createFromGlobals(); $response = new Response(); 

Let's look at it in action:
  <?php // framework/index.php require_once __DIR__.'/init.php'; $input = $request->get('name', 'World'); $response->setContent(sprintf('Hello %s', htmlspecialchars($input, ENT_QUOTES, 'UTF-8'))); $response->send(); 

And for the " Goodbye " page:
  <?php // framework/bye.php require_once __DIR__.'/init.php'; $response->setContent('Goodbye!'); $response->send(); 

So, we collected the common code in one place. But that doesn't seem like a good level of abstraction, is it?
First, we still have a call to the send () method on all pages, so our pages do not look like regular templates, and we still cannot “correctly” test the code.
')
Another serious problem is that adding a new page means that we have to create a new PHP script,
the name of which will be transmitted to the URL (http://example.com/bye.php) and called by the web server.
A good idea would be to transfer the call of the desired script into our code for better flexibility.
This can be achieved by redirecting all requests to a single PHP script - front controller.



The script itself may look like this:
  <?php // framework/front.php require_once __DIR__.'/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; $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 front.php script, $ map links paths to URLs to corresponding paths to script files.

As a bonus, if the client requests a path that is not defined in the array of matches, we return a 404 error.
Now you control the site.

Now, to access the pages, you should use the front.php front controller script :


/ hello and / bye are the page paths.

Hint.
Most web servers, such as Apache or nginx, are capable of rewriting
the requested URLs of the pages thereby removing the name of the front controller file from them ( front.php ),
so your users will only need to type example.com/hello?name=Fabien , which looks much better.


So the focus is on using the Request :: getPathInfo () method which returns part of the path from the " Request "
deleting from it the name of the front controller file including all subdirectories (only if necessary - see the hint above).

You do not even need to configure a web server. Simply
replace the call $ request = Request :: createFromGlobals (); something like $ request = Request :: create ('/ hello? name = Fabien'); ,
where the argument is the query you want to imitate.

Now that all requests to our pages go through one script (front.php),
we can contribute to security by transferring all other PHP files outside the root web directory:
     example.com
     + - composer.json
     ¦ src
     ¦ + - autoload.php
     ¦ L-- pages
     ¦ + - hello.php
     ¦ L-- bye.php
     + - vendor
     L-- web
         L-- front.php

Now, configure your web server so that the web / directory is the root for it.
All 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 repeats on every page is the call to setContent () .
Now we can turn all our pages into “templates” by simply rendering content.
and call 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 hello.php script 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') ?> 

Today, our framework looks like this:
  <?php // example.com/web/front.php require_once __DIR__.'/../src/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; $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 takes place in two steps: add an element to the array of matches and create
PHP template in src / pages / . From the template, we have access to the data " Request " through the variable $ request
and we can manipulate the response headers (" Response ") through the $ response variable.

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

Source: https://habr.com/ru/post/136471/


All Articles