Passed
Pull Request — master (#59)
by
unknown
28:20 queued 13:14
created

BaseController   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 116
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 22
eloc 59
dl 0
loc 116
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A prepareResponse() 0 18 6
A __construct() 0 7 1
A isAjax() 0 4 1
A handleError() 0 3 1
B handle() 0 36 9
A process() 0 13 4
1
<?php
2
3
namespace App\LazyRendering\Http;
4
5
use Generator;
6
use Psr\Container\ContainerInterface as Container;
7
use Psr\Http\Message\ResponseFactoryInterface as ResponseFactory;
8
use Psr\Http\Message\ResponseInterface as Response;
9
use Psr\Http\Message\ServerRequestInterface as Request;
10
use Psr\Http\Message\StreamFactoryInterface;
11
use Psr\Http\Server\MiddlewareInterface;
12
use Psr\Http\Server\RequestHandlerInterface;
13
use Throwable;
14
use Yiisoft\Injector\Injector;
15
use Yiisoft\Router\UrlGeneratorInterface;
16
use Yiisoft\Widget\WidgetFactory;
17
18
abstract class BaseController implements MiddlewareInterface, RequestHandlerInterface
19
{
20
    protected ResponseFactory $responseFactory;
21
    protected Request $request;
22
    protected UrlGeneratorInterface $urlGenerator;
23
    /** @var null|mixed Layout definition with method render() */
24
    protected $pageLayout = null;
25
    protected StreamFactoryInterface $streamFactory;
26
27
    private Container $container;
28
29
    /**
30
     * baseController constructor.
31
     * @param Container $container
32
     * @param mixed[]   $options
33
     */
34
    public function __construct(Container $container)
35
    {
36
        $this->container = $container;
37
        $this->responseFactory = $this->container->get(ResponseFactory::class);
38
        $this->urlGenerator = $this->container->get(UrlGeneratorInterface::class);
39
        $this->streamFactory = $this->container->get(StreamFactoryInterface::class);
40
        WidgetFactory::initialize($container);
41
    }
42
43
    /**
44
     * @param Request  $request
45
     * @param Response $response
46
     * @param array    $args
47
     * @return Response
48
     * @throws HttpException
49
     * @throws Throwable
50
     */
51
    public function process(Request $request, RequestHandlerInterface $handler): Response
52
    {
53
        # disable output buffering
54
        for ($j = ob_get_level(), $i = 0; $i < $j; ++$i) {
55
            ob_end_flush();
56
        }
57
58
        try {
59
            return $this->handle($request);
60
        } catch (HttpNotFoundException $e) {
61
            return $handler->handle($request);
62
        } catch (Throwable $e) {
63
            return $this->handleError($e);
64
        }
65
    }
66
67
    public function handle(Request $request): Response
68
    {
69
        $this->request = $request;
70
        $args = $request->getAttributes();
71
        $method = strtoupper($request->getMethod());
72
        $page = $args['page'] ?? 'index';
73
74
75
        $action = $args['action'] ?? $request->getParsedBody()['action'] ?? $request->getQueryParams()['action'] ?? null;
76
        # find action
77
        if ($action !== null or $method === 'POST') {
78
            $action = $action ?? $page;
79
            $method = 'action' . str_replace(' ', '', ucwords($action));
80
        } elseif ($method === 'GET') {
81
            $method = 'page' . str_replace(' ', '', ucwords($page));
82
        }
83
        if (!method_exists($this, $method)) {
84
            throw new HttpNotFoundException($request);
85
        }
86
        $data = (new Injector($this->container))->invoke([$this, $method]);
87
88
        $response = $data instanceof Response ? $data : $this->prepareResponse($data);
89
90
        // Force Buffering (classic mode)
91
        if (($this->request->getQueryParams()['forceBuffering'] ?? 0) === '1') {
92
            $content = $response->getBody()->getContents();
93
            $stream = $this->streamFactory->createStream($content);
94
            return $response->withBody($stream);
95
        } elseif (($this->request->getQueryParams()['forceBuffering'] ?? 0) === '2') {
96
            $stream = $response->getBody();
97
            if (!$stream instanceof GeneratorStream) {
98
                throw new \Exception('Combined mode not supported');
99
            }
100
            $stream->setReadMode(GeneratorStream::READ_MODE_FIRST_YIELD);
101
        }
102
        return $response;
103
    }
104
105
    public function isAjax(): bool
106
    {
107
        $headers = $this->request->getHeader('x-requested-with');
108
        return in_array('XMLHttpRequest', $headers);
109
    }
110
111
    protected function prepareResponse(iterable $page): Response
112
    {
113
        if (!$page instanceof Generator) {
114
            $page = (static function (iterable $iterable) {
115
                yield from $iterable;
116
            })($page);
117
        }
118
        // Add layout rendering
119
        if ($this->pageLayout !== null) {
120
            if (is_string($this->pageLayout)) {
121
                $this->pageLayout = $this->container->get($this->pageLayout);
122
            } elseif (!is_object($this->pageLayout) || !method_exists($this->pageLayout, 'render')) {
123
                throw new \RuntimeException('Bad Layout definition');
124
            }
125
            $page = (new Injector($this->container))->invoke([$this->pageLayout, 'render'], [$page, $this->request]);
126
        }
127
        $stream = new GeneratorStream($page);
128
        return $this->responseFactory->createResponse()->withBody($stream);
129
    }
130
131
    protected function handleError(Throwable $error): Response
132
    {
133
        throw $error;
134
    }
135
}
136