1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2022 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees\Factories; 21 22use Fig\Http\Message\StatusCodeInterface; 23use Fisharebest\Webtrees\Contracts\ResponseFactoryInterface; 24use Fisharebest\Webtrees\Module\ModuleThemeInterface; 25use Fisharebest\Webtrees\Registry; 26use Fisharebest\Webtrees\Webtrees; 27use Psr\Http\Message\ResponseFactoryInterface as PSR17ResponseFactoryInterface; 28use Psr\Http\Message\ResponseInterface; 29use Psr\Http\Message\ServerRequestInterface; 30use Psr\Http\Message\StreamFactoryInterface; 31use Psr\Http\Message\UriInterface; 32 33use function app; 34use function is_string; 35use function json_encode; 36use function view; 37 38use const JSON_THROW_ON_ERROR; 39use const JSON_UNESCAPED_UNICODE; 40 41/** 42 * Make a PSR-7 response (using a PSR-17 response factory). 43 */ 44class ResponseFactory implements ResponseFactoryInterface 45{ 46 private PSR17ResponseFactoryInterface $response_factory; 47 48 private StreamFactoryInterface $stream_factory; 49 50 /** 51 * @param PSR17ResponseFactoryInterface $response_factory 52 * @param StreamFactoryInterface $stream_factory 53 */ 54 public function __construct(PSR17ResponseFactoryInterface $response_factory, StreamFactoryInterface $stream_factory) 55 { 56 $this->response_factory = $response_factory; 57 $this->stream_factory = $stream_factory; 58 } 59 60 /** 61 * Redirect to a named route. 62 * 63 * @param string $route_name 64 * @param array<bool|int|string|array<string>|null> $parameters 65 * @param int $status 66 * 67 * @return ResponseInterface 68 * 69 */ 70 public function redirect( 71 string $route_name, 72 array $parameters = [], 73 int $status = StatusCodeInterface::STATUS_FOUND 74 ): ResponseInterface { 75 $url = Registry::routeFactory()->route($route_name, $parameters); 76 77 return $this->redirectUrl($url, $status); 78 } 79 80 /** 81 * Redirect to a URL. 82 * 83 * @param string|UriInterface $url 84 * @param int $code 85 * 86 * @return ResponseInterface 87 */ 88 public function redirectUrl($url, int $code = StatusCodeInterface::STATUS_FOUND): ResponseInterface 89 { 90 return $this->response_factory 91 ->createResponse($code) 92 ->withHeader('location', (string) $url); 93 } 94 95 /** 96 * @param string|array<mixed>|object $content 97 * @param int $code 98 * @param array<string,string> $headers 99 * 100 * @return ResponseInterface 101 */ 102 public function response($content = '', int $code = StatusCodeInterface::STATUS_OK, array $headers = []): ResponseInterface 103 { 104 if ($content === '' && $code === StatusCodeInterface::STATUS_OK) { 105 $code = StatusCodeInterface::STATUS_NO_CONTENT; 106 } 107 108 if (is_string($content)) { 109 $headers['content-type'] ??= 'text/html; charset=UTF-8'; 110 } else { 111 $content = json_encode($content, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); 112 $headers['content-type'] ??= 'application/json'; 113 } 114 115 $stream = $this->stream_factory->createStream($content); 116 117 $response = $this->response_factory 118 ->createResponse($code) 119 ->withBody($stream); 120 121 foreach ($headers as $key => $value) { 122 $response = $response->withHeader($key, $value); 123 } 124 125 return $response; 126 } 127 128 /** 129 * Create and render a view, and embed it in an HTML page. 130 * 131 * @param string $view_name 132 * @param array<string,mixed> $view_data 133 * @param int $status 134 * @param string $layout_name 135 * 136 * @return ResponseInterface 137 */ 138 public function view( 139 string $view_name, 140 array $view_data, 141 int $status = StatusCodeInterface::STATUS_OK, 142 string $layout_name = Webtrees::LAYOUT_DEFAULT 143 ): ResponseInterface { 144 // Render the view. 145 $content = view($view_name, $view_data); 146 147 // Make the view's data available to the layout. 148 $layout_data = [ 149 'content' => $content, 150 'request' => app(ServerRequestInterface::class), 151 'theme' => app(ModuleThemeInterface::class), 152 'title' => $view_data['title'] ?? Webtrees::NAME, 153 ]; 154 155 // Embed the content in the layout. 156 $html = view($layout_name, $layout_data); 157 158 return $this->response($html, $status); 159 } 160} 161