Welcome to Admin Junkies, Guest — join our community!

Register or log in to explore all our content and services for free on Admin Junkies.

PHP Web Application Development: Http Message Review - ServerRequest Usage

Tyrsson

Retired Staff
Retired Staff
Joined
Apr 29, 2023
Messages
402
Website
github.com
Credits
1,098
As you are probably aware the heart of web application development is http messages.

There are always two parts, the Request and the Response.
From the Fig Psr https://www.php-fig.org/psr/psr-7/

Request
Code:
POST /path HTTP/1.1
Host: example.com

foo=bar&baz=bat

Response
Code:
HTTP/1.1 200 OK
Content-Type: text/plain

This is the response body

Messages have a lot more data that is sent along with each depending on whether is a request or response. This could be headers, cookies, response codes etc. For most web applications the request is what starts your application processing. When thinking of applications in terms of the Psr's it goes something like this:

Incoming Http Request
Psr11 Container initialization / bootstrapping
Psr7 RequestInterface instance initialization
Psr15 RequestHandler processing
Psr7 ResponseInterface initialization
Response returned
script exit

First take a look at the RequestFactory since this class is responsible for creating our ServerRequest instance and seeding it with a Uri instance.
RequestFactory.php

So, how does using Laminas Diactoros (which is a RequestInterface/ResponseInterface implementation) library help us when handling request and responses? First let's look at the ServerRequest objects state just after initialization.
Given a url with the form of:


This is a section of what is returned if you var_dump($request) given that $request is a ServerRequest instance that has been created via the ServerRequestFactory::fromGlobals() method, which it should be in most circumstances. Note that the usage of ServerRequestFactory is within RequestFactory.
PHP:
  private array 'attributes' =>
    array (size=1)
      'uri' =>
        object(Laminas\Diactoros\Uri)[15]
          protected 'allowedSchemes' =>
            array (size=2)
              ...
          private string 'scheme' => string 'http' (length=4)
          private string 'userInfo' => string '' (length=0)
          private string 'host' => string 'twitch.local' (length=12)
          private ?int 'port' => null
          private string 'path' => string '/profile' (length=8)
          private string 'query' => string 'user=tyrsson&action=update' (length=26)
          private string 'fragment' => string '' (length=0)
          private ?string 'uriString' => null

What this allows is for you to interact with the request. An example that comes to mind would be we need the user passed in the query string.
PHP:
$params = $request->getQueryParams();

For the above url the $params would then contain:
PHP:
'user' => string 'tyrsson' (length=7)
'action' => string 'update' (length=6)

Security Note:
This code does not provide examples of input filtering etc. It should only be used as examples to learn the general concepts. We will cover security later.

Now, here is where it gets really awesome. Say, that based on what happens when tyrsson views his profile, say we are going to need some of the data later in processing. Well, we can add that data to the $request object as an attribute. So for example if we look at the revised code (that I will commit to the repo shortly) from the Kernel::__construct method we can see how this can be accomplished.

PHP:
    private const USER_PARAM_KEY = 'user';
    public const  REQUESTED_USER = 'requestedUser';
    private array $queryParams;

    public function __construct(
        private ServerRequest $request,
        private View $view,
        private array $config
    ) {
        $this->queryParams = $this->request->getQueryParams();
        if (
            isset($this->queryParams[self::USER_PARAM_KEY])
            && isset($this->config['user-data'][$this->queryParams[self::USER_PARAM_KEY]])
        ) {
            $this->request = $this->request->withAttribute(
                'requestedUser',
                $this->config['user-data'][$this->queryParams[self::USER_PARAM_KEY]]
            );
        }
    }

Now, with this in place at any future point in processing we can retrieve the requested users data from the request object. I make that data available to the template layer in the following code snippet. It's important to note that the user data will be available via the request object anywhere in the future of the application lifecycle, but it will only be available to the template layer within the scope of the /profile page.
PHP:
protected function getModel(string $page = null): ModelInterface
    {
        $model = new ViewModel();
        if ($page !== null && $page !== '') {
            $model->setTemplate($page);
        } else {
            $model->setTemplate('home');
        }
        $model->setVariables($this->config['app_settings']['page_data'][$model->getTemplate()] ?? []);
        if ($model->getTemplate() === 'profile') {
            $model->setVariable(
                self::USER_PARAM_KEY,
                $this->request->getAttribute(self::REQUESTED_USER)
            );
        }
        return $model;
    }

So that's all great and good and all, but how does it figure out which page to load? Well, we get that information from the ServerRequest object as well since we are not using a Router in this application.

So with the current setup we need to load a page based on the path. Well lucky for us ServerRequest has us covered. Basically the following method just gets a Uri object from the ServerRequest object and we then call getPath. The problem is that it will be /profile. So we do a little string handling and remove the / and were set. The only requirement is that our template file has to match what we are requesting exactly. Since we have a MenuHelper that takes care of building the main navigation based on the available template files we always have the correct path in the link.

PHP:
protected function getRequestedPage(): string
    {
        $uri    = $this->request->getUri();
        $page = $uri->getPath();
        return substr($page, 1, (strlen($page) - 1));
    }

So, as simple as it is in this stage of development, that is pretty much it for how the Request is being used to drive the application. Thanks for reading and as always, if you see something that can be improved or something I missed please post it up in the replies along with any questions.
 
Last edited:

Log in or register to unlock full forum benefits!

Log in or register to unlock full forum benefits!

Register

Register on Admin Junkies completely free.

Register now
Log in

If you have an account, please log in

Log in
Activity
So far there's no one here

Users who are viewing this thread

Would You Rather #9

  • Start a forum in a popular but highly competitive niche

    Votes: 5 21.7%
  • Initiate a forum within a limited-known niche with zero competition

    Votes: 18 78.3%
Win this space by entering the Website of The Month Contest

Theme editor

Theme customizations

Graphic Backgrounds

Granite Backgrounds