1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2023 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 is_string; 34use function json_encode; 35use function view; 36 37use const JSON_THROW_ON_ERROR; 38use const JSON_UNESCAPED_UNICODE; 39 40/** 41 * Make a PSR-7 response (using a PSR-17 response factory). 42 */ 43class ResponseFactory implements ResponseFactoryInterface 44{ 45 private PSR17ResponseFactoryInterface $response_factory; 46 47 private StreamFactoryInterface $stream_factory; 48 49 /** 50 * @param PSR17ResponseFactoryInterface $response_factory 51 * @param StreamFactoryInterface $stream_factory 52 */ 53 public function __construct(PSR17ResponseFactoryInterface $response_factory, StreamFactoryInterface $stream_factory) 54 { 55 $this->response_factory = $response_factory; 56 $this->stream_factory = $stream_factory; 57 } 58 59 /** 60 * Redirect to a named route. 61 * 62 * @param string $route_name 63 * @param array<bool|int|string|array<string>|null> $parameters 64 * @param int $status 65 * 66 * @return ResponseInterface 67 * 68 */ 69 public function redirect( 70 string $route_name, 71 array $parameters = [], 72 int $status = StatusCodeInterface::STATUS_FOUND 73 ): ResponseInterface { 74 $url = Registry::routeFactory()->route($route_name, $parameters); 75 76 return $this->redirectUrl($url, $status); 77 } 78 79 /** 80 * Redirect to a URL. 81 * 82 * @param UriInterface|string $url 83 * @param int $code 84 * 85 * @return ResponseInterface 86 */ 87 public function redirectUrl(UriInterface|string $url, int $code = StatusCodeInterface::STATUS_FOUND): ResponseInterface 88 { 89 return $this->response_factory 90 ->createResponse($code) 91 ->withHeader('location', (string) $url); 92 } 93 94 /** 95 * @param string|array<mixed>|object $content 96 * @param int $code 97 * @param array<string,string> $headers 98 * 99 * @return ResponseInterface 100 */ 101 public function response(string|array|object $content = '', int $code = StatusCodeInterface::STATUS_OK, array $headers = []): ResponseInterface 102 { 103 if ($content === '' && $code === StatusCodeInterface::STATUS_OK) { 104 $code = StatusCodeInterface::STATUS_NO_CONTENT; 105 } 106 107 if (is_string($content)) { 108 $headers['content-type'] ??= 'text/html; charset=UTF-8'; 109 } else { 110 $content = json_encode($content, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); 111 $headers['content-type'] ??= 'application/json'; 112 } 113 114 $stream = $this->stream_factory->createStream($content); 115 116 $response = $this->response_factory 117 ->createResponse($code) 118 ->withBody($stream); 119 120 foreach ($headers as $key => $value) { 121 $response = $response->withHeader($key, $value); 122 } 123 124 return $response; 125 } 126 127 /** 128 * Create and render a view, and embed it in an HTML page. 129 * 130 * @param string $view_name 131 * @param array<string,mixed> $view_data 132 * @param int $status 133 * @param string $layout_name 134 * 135 * @return ResponseInterface 136 */ 137 public function view( 138 string $view_name, 139 array $view_data, 140 int $status = StatusCodeInterface::STATUS_OK, 141 string $layout_name = Webtrees::LAYOUT_DEFAULT 142 ): ResponseInterface { 143 // Render the view. 144 $content = view($view_name, $view_data); 145 146 // Make the view's data available to the layout. 147 $layout_data = [ 148 'content' => $content, 149 'request' => Registry::container()->get(ServerRequestInterface::class), 150 'theme' => Registry::container()->get(ModuleThemeInterface::class), 151 'title' => $view_data['title'] ?? Webtrees::NAME, 152 ]; 153 154 // Embed the content in the layout. 155 $html = view($layout_name, $layout_data); 156 157 return $this->response($html, $status); 158 } 159} 160