WebsiteMiddleware::__construct()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 5
dl 0
loc 9
ccs 7
cts 7
cp 1
crap 2
rs 9.9666
c 0
b 0
f 0
1
<?php
2
namespace Germania\PsrWebsites;
3
4
use Psr\Log\LoggerInterface;
5
use Psr\Log\NullLogger;
6
use Psr\Http\Message\ServerRequestInterface as Request;
7
use Psr\Http\Message\ResponseInterface as Response;
8
use Psr\Container\ContainerInterface;
9
use Slim\Http\Body as ResponseBody;
10
11
class WebsiteMiddleware
12
{
13
14
    /**
15
     * @var ContainerInterface
16
     */
17
    public $websites;
18
19
    /**
20
     * @var Callable
21
     */
22
    public $render;
23
24
    /**
25
     * @var string
26
     */
27
    public $template;
28
29
    /**
30
     * @var array
31
     */
32
    public $defaults = array();
33
34
    /**
35
     * @var LoggerInterface
36
     */
37
    public $logger;
38
39
40
41
42
    /**
43
     * @param ContainerInterface  $websites   A ContainerInterface instance that returns a website for the current route name
44
     * @param Callable            $render     Rendering callable that receives template file and variables context
45
     * @param string              $template   Template file
46
     * @param array               $defaults   Default variables context
47
     * @param LoggerInterface     $logger     Optional: PSR-3 Logger
48
     */
49 6
    public function __construct( ContainerInterface $websites, Callable $render, $template, array $defaults, LoggerInterface $logger = null)
50
    {
51 6
        $this->websites  = $websites;
52 6
        $this->render    = $render;
53 6
        $this->template  = $template;
54 6
        $this->defaults  = $defaults;
55
56 6
        $this->logger    = $logger ?: new NullLogger;
57 6
    }
58
59
60
    /**
61
     * @param  Psr\Http\Message\ServerRequestInterface  $request  PSR7 request
62
     * @param  Psr\Http\Message\ResponseInterface       $response PSR7 response
63
     * @param  callable                                 $next     Next middleware
64
     *
65
     * @return Psr\Http\Message\ResponseInterface
66
     */
67 5
    public function __invoke(Request $request, Response $response, $next)
68
    {
69
        // ---------------------------------------
70
        //  1. Get current Website object,
71
        //     add as attribute to Request.
72
        //     The website will be available within route controllers.
73
        // ---------------------------------------
74 5
        $current_route_name = $request->getAttribute('route')->getName();
75
76 5
        $website_factory = $this->websites;
77 5
        $website = $website_factory->get( $current_route_name );
78
79 5
        $request = $request->withAttribute('website', $website);
80
81
82
        // ---------------------------------------
83
        //  2. Call next middleware.
84
        //     Page content (not HTML head and stuff!)
85
        //     should be created there...
86
        // ---------------------------------------
87 5
        $content_response = $next($request, $response);
88
89 5
        $this->logger->debug("Content response", array_merge(
90 5
            [ 'status' => $content_response->getStatusCode() ],
91 5
            $content_response->getHeaders()
92 1
        ));
93
94
        // ---------------------------------------
95
        // 3. Create template variables
96
        // ---------------------------------------
97
98 5
        $vars = array_merge(
99 5
            $this->defaults, [
100 5
                'title'    =>  $website->getTitle(),
101 5
                'id'       =>  $website->getDomId(),
102 5
                'base_url' =>  $request->getUri()->getBaseUrl(),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Http\Message\UriInterface as the method getBaseUrl() does only exist in the following implementations of said interface: Slim\Http\Uri.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
103 5
                'content'  =>  (string) $content_response->getBody()
104 1
            ]
105 1
        );
106
107 5
        $vars['javascripts'] = isset($vars['javascripts'])
108 1
        ? array_merge($vars['javascripts'], $website->getJavascripts() ?: array())
109 5
        : ($website->getJavascripts() ?: array());
110
111 5
        $vars['stylesheets'] = isset($vars['stylesheets'])
112 1
        ? array_merge($vars['stylesheets'], $website->getStylesheets() ?: array())
113 5
        : ($website->getStylesheets() ?: array());
114
115
116
        // ---------------------------------------
117
        // 5. After that, render 'full page' template
118
        //    and store in new Response Body.
119
        // ---------------------------------------
120
121
        // Output global website template
122 5
        $this->logger->debug("Render page template…");
123
124 5
        $render = $this->render;
125 5
        $rendered_result = $render( $this->template, $vars);
126
127 5
        $this->logger->debug("Finish page template render; write response");
128
129 5
        $full_html_response_body = new ResponseBody(fopen('php://temp', 'r+'));
130 5
        $full_html_response_body->write($rendered_result);
131
132
133
        // ---------------------------------------
134
        // 6. Replace old Response Body with new one
135
        // ---------------------------------------
136
137 5
        return $content_response->withHeader('Content-Type', 'text/html')
138 5
                                ->withBody($full_html_response_body);
139
    }
140
141
142
143
}
144