Passed
Pull Request — master (#59)
by Alexander
26:34 queued 11:31
created

BaseController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
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\Http\Method;
15
use Yiisoft\Injector\Injector;
16
use Yiisoft\Router\UrlGeneratorInterface;
17
use Yiisoft\Widget\WidgetFactory;
18
19
abstract class BaseController implements MiddlewareInterface
20
{
21
    protected ResponseFactory $responseFactory;
22
    protected Request $request;
23
    protected UrlGeneratorInterface $urlGenerator;
24
    /** @var null|mixed Layout definition with method render() */
25
    protected $pageLayout = null;
26
    protected StreamFactoryInterface $streamFactory;
27
28
    private Container $container;
29
30
    /**
31
     * baseController constructor.
32
     * @param Container $container
33
     * @param mixed[] $options
34
     */
35
    public function __construct(Container $container)
36
    {
37
        $this->container = $container;
38
        $this->responseFactory = $this->container->get(ResponseFactory::class);
39
        $this->urlGenerator = $this->container->get(UrlGeneratorInterface::class);
40
        $this->streamFactory = $this->container->get(StreamFactoryInterface::class);
41
        WidgetFactory::initialize($container);
42
    }
43
44
    /**
45
     * @param Request $request
46
     * @param Response $response
47
     * @param array $args
48
     * @return Response
49
     * @throws HttpException
50
     * @throws Throwable
51
     */
52
    public function process(Request $request, RequestHandlerInterface $handler): Response
53
    {
54
        # disable output buffering
55
        for ($j = ob_get_level(), $i = 0; $i < $j; ++$i) {
56
            ob_end_flush();
57
        }
58
59
        return $this->handle($request) ?? $handler->handle($request);
60
    }
61
62
    public function handle(Request $request): ?Response
63
    {
64
        $this->request = $request;
65
        $args = $request->getAttributes();
66
        $method = strtoupper($request->getMethod());
67
        $page = $args['page'] ?? 'index';
68
69
70
        $action = $args['action'] ?? $request->getParsedBody()['action'] ?? $request->getQueryParams()['action'] ?? null;
71
        # find action
72
        if ($action !== null || $method === Method::POST) {
73
            $action = $action ?? $page;
74
            $method = 'action' . str_replace(' ', '', ucwords($action));
75
        } elseif ($method === Method::GET) {
76
            $method = 'page' . str_replace(' ', '', ucwords($page));
77
        }
78
        if (!method_exists($this, $method)) {
79
            return null;
80
        }
81
        $data = (new Injector($this->container))->invoke([$this, $method]);
82
83
        $response = $data instanceof Response ? $data : $this->prepareResponse($data);
84
85
        // Force Buffering (classic mode)
86
        if (($this->request->getQueryParams()['forceBuffering'] ?? 0) === '1') {
87
            // Buffering from generator
88
            $content = $response->getBody()->getContents();
89
            $stream = $this->streamFactory->createStream($content);
90
            return $response->withBody($stream);
91
        }
92
93
        if (($this->request->getQueryParams()['forceBuffering'] ?? 0) === '2') {
94
            $stream = $response->getBody();
95
            if (!$stream instanceof GeneratorStream) {
96
                throw new \Exception('Combined mode not supported');
97
            }
98
            $stream->setReadMode(GeneratorStream::READ_MODE_FIRST_YIELD);
99
        }
100
        return $response;
101
    }
102
103
    protected function prepareResponse(iterable $page): Response
104
    {
105
        if (!$page instanceof Generator) {
106
            $page = (static function (iterable $iterable) {
107
                yield from $iterable;
108
            })($page);
109
        }
110
        // Add layout rendering
111
        if ($this->pageLayout !== null) {
112
            if (is_string($this->pageLayout)) {
113
                $this->pageLayout = $this->container->get($this->pageLayout);
114
            } elseif (!is_object($this->pageLayout) || !method_exists($this->pageLayout, 'render')) {
115
                throw new \RuntimeException('Bad Layout definition');
116
            }
117
            $page = (new Injector($this->container))->invoke([$this->pageLayout, 'render'], [$page, $this->request]);
118
        }
119
        $stream = new GeneratorStream($page);
120
        return $this->responseFactory->createResponse()->withBody($stream);
121
    }
122
}
123