Version 5.1 - All the GraphQL #32

Closed
timw4mail wants to merge 1160 commits from develop into master
19 changed files with 126 additions and 86 deletions
Showing only changes of commit c100105fbc - Show all commits

View File

@ -64,7 +64,7 @@ if (array_key_exists('timezone', $checkedConfig) && ! empty($checkedConfig['time
{ {
date_default_timezone_set($checkedConfig['timezone']); date_default_timezone_set($checkedConfig['timezone']);
} }
else if ($timezone !== '') else if (is_string($timezone) && $timezone !== '')
{ {
date_default_timezone_set($timezone); date_default_timezone_set($timezone);
} }

View File

@ -8,6 +8,7 @@ parameters:
- ./console - ./console
- index.php - index.php
ignoreErrors: ignoreErrors:
- '#Access to an undefined property Aviat\\Ion\\Friend#'
- "#Offset 'fields' does not exist on array#" - "#Offset 'fields' does not exist on array#"
- '#Call to an undefined method Aura\\\Html\\\HelperLocator::[a-zA-Z0-9_]+\(\)#' - '#Call to an undefined method Aura\\\Html\\\HelperLocator::[a-zA-Z0-9_]+\(\)#'
- '#Call to an undefined method Query\\QueryBuilderInterface::[a-zA-Z0-9_]+\(\)#' - '#Call to an undefined method Query\\QueryBuilderInterface::[a-zA-Z0-9_]+\(\)#'

View File

@ -91,7 +91,7 @@ function loadTomlFile(string $filename): array
return Toml::parseFile($filename); return Toml::parseFile($filename);
} }
function _iterateToml(TomlBuilder $builder, iterable $data, $parentKey = NULL): void function _iterateToml(TomlBuilder $builder, iterable $data, mixed $parentKey = NULL): void
{ {
foreach ($data as $key => $value) foreach ($data as $key => $value)
{ {
@ -359,7 +359,7 @@ function colNotEmpty(array $search, string $key): bool
* *
* @param CacheInterface $cache * @param CacheInterface $cache
* @return bool * @return bool
* @throws InvalidArgumentException * @throws Throwable
*/ */
function clearCache(CacheInterface $cache): bool function clearCache(CacheInterface $cache): bool
{ {

View File

@ -203,7 +203,7 @@ class Controller {
* @throws NotFoundException * @throws NotFoundException
* @return string * @return string
*/ */
protected function loadPartial($view, string $template, array $data = []): string protected function loadPartial(HtmlView $view, string $template, array $data = []): string
{ {
$router = $this->container->get('dispatcher'); $router = $this->container->get('dispatcher');
@ -236,7 +236,7 @@ class Controller {
* @throws ContainerException * @throws ContainerException
* @throws NotFoundException * @throws NotFoundException
*/ */
protected function renderFullPage($view, string $template, array $data): HtmlView protected function renderFullPage(HtmlView $view, string $template, array $data): HtmlView
{ {
$csp = [ $csp = [
"default-src 'self'", "default-src 'self'",
@ -360,7 +360,7 @@ class Controller {
* @throws NotFoundException * @throws NotFoundException
* @return string * @return string
*/ */
protected function showMessage($view, string $type, string $message): string protected function showMessage(HtmlView $view, string $type, string $message): string
{ {
return $this->loadPartial($view, 'message', [ return $this->loadPartial($view, 'message', [
'message_type' => $type, 'message_type' => $type,
@ -399,7 +399,7 @@ class Controller {
* @throws DoubleRenderException * @throws DoubleRenderException
* @return void * @return void
*/ */
protected function outputJSON($data, int $code): void protected function outputJSON(mixed $data, int $code): void
{ {
(new JsonView()) (new JsonView())
->setOutput($data) ->setOutput($data)
@ -420,10 +420,7 @@ class Controller {
{ {
(new HttpView())->redirect($url, $code)->send(); (new HttpView())->redirect($url, $code)->send();
} }
catch (\Throwable $e) catch (\Throwable) {}
{
}
} }
} }
// End of BaseController.php // End of BaseController.php

View File

@ -145,7 +145,7 @@ final class Anime extends BaseController {
{ {
$this->checkAuth(); $this->checkAuth();
$data = $this->request->getParsedBody(); $data = (array)$this->request->getParsedBody();
if (empty($data['mal_id'])) if (empty($data['mal_id']))
{ {
@ -220,7 +220,7 @@ final class Anime extends BaseController {
{ {
$this->checkAuth(); $this->checkAuth();
$data = $this->request->getParsedBody(); $data = (array)$this->request->getParsedBody();
// Do some minor data manipulation for // Do some minor data manipulation for
// large form-based updates // large form-based updates
@ -257,7 +257,7 @@ final class Anime extends BaseController {
} }
else else
{ {
$data = $this->request->getParsedBody(); $data = (array)$this->request->getParsedBody();
} }
if (empty($data)) if (empty($data))
@ -282,7 +282,7 @@ final class Anime extends BaseController {
{ {
$this->checkAuth(); $this->checkAuth();
$body = $this->request->getParsedBody(); $body = (array)$this->request->getParsedBody();
$response = $this->model->deleteLibraryItem($body['id'], $body['mal_id']); $response = $this->model->deleteLibraryItem($body['id'], $body['mal_id']);
if ($response === TRUE) if ($response === TRUE)

View File

@ -154,7 +154,7 @@ final class AnimeCollection extends BaseController {
public function edit(): void public function edit(): void
{ {
$this->checkAuth(); $this->checkAuth();
$this->update($this->request->getParsedBody()); $this->update((array)$this->request->getParsedBody());
} }
/** /**
@ -169,7 +169,7 @@ final class AnimeCollection extends BaseController {
{ {
$this->checkAuth(); $this->checkAuth();
$data = $this->request->getParsedBody(); $data = (array)$this->request->getParsedBody();
if (array_key_exists('id', $data)) if (array_key_exists('id', $data))
{ {
// Check for existing entry // Check for existing entry
@ -218,7 +218,7 @@ final class AnimeCollection extends BaseController {
{ {
$this->checkAuth(); $this->checkAuth();
$data = $this->request->getParsedBody(); $data = (array)$this->request->getParsedBody();
if ( ! array_key_exists('hummingbird_id', $data)) if ( ! array_key_exists('hummingbird_id', $data))
{ {
$this->setFlashMessage("Can't delete item that doesn't exist", 'error'); $this->setFlashMessage("Can't delete item that doesn't exist", 'error');
@ -238,11 +238,11 @@ final class AnimeCollection extends BaseController {
/** /**
* Update a collection item * Update a collection item
* *
* @param $data * @param array $data
* @throws ContainerException * @throws ContainerException
* @throws NotFoundException * @throws NotFoundException
*/ */
protected function update($data): void protected function update(array $data): void
{ {
if (array_key_exists('hummingbird_id', $data)) if (array_key_exists('hummingbird_id', $data))
{ {

View File

@ -37,9 +37,7 @@ final class Images extends BaseController {
* @param string $file The filename to look for * @param string $file The filename to look for
* @param bool $display Whether to output the image to the server * @param bool $display Whether to output the image to the server
* @return void * @return void
* @throws NotFoundException
* @throws Throwable * @throws Throwable
* @throws ContainerException
*/ */
public function cache(string $type, string $file, $display = TRUE): void public function cache(string $type, string $file, $display = TRUE): void
{ {
@ -134,7 +132,16 @@ final class Images extends BaseController {
[$origWidth] = getimagesizefromstring($data); [$origWidth] = getimagesizefromstring($data);
$gdImg = imagecreatefromstring($data); $gdImg = imagecreatefromstring($data);
if ($gdImg === FALSE)
{
return;
}
$resizedImg = imagescale($gdImg, $width ?? $origWidth); $resizedImg = imagescale($gdImg, $width ?? $origWidth);
if ($resizedImg === FALSE)
{
return;
}
if ($ext === 'gif') if ($ext === 'gif')
{ {
@ -161,7 +168,7 @@ final class Images extends BaseController {
? 'image/webp' ? 'image/webp'
: $response->getHeader('content-type')[0]; : $response->getHeader('content-type')[0];
$outputFile = (strpos($file, '-original') !== FALSE) $outputFile = (str_contains($file, '-original'))
? "{$filePrefix}-original.{$ext}" ? "{$filePrefix}-original.{$ext}"
: "{$filePrefix}.{$ext}"; : "{$filePrefix}.{$ext}";

View File

@ -135,15 +135,13 @@ final class Manga extends Controller {
* Add an manga to the list * Add an manga to the list
* *
* @return void * @return void
* @throws NotFoundException
* @throws Throwable * @throws Throwable
* @throws ContainerException
*/ */
public function add(): void public function add(): void
{ {
$this->checkAuth(); $this->checkAuth();
$data = $this->request->getParsedBody(); $data = (array)$this->request->getParsedBody();
if ( ! array_key_exists('id', $data)) if ( ! array_key_exists('id', $data))
{ {
$this->redirect('manga/add', 303); $this->redirect('manga/add', 303);
@ -163,7 +161,7 @@ final class Manga extends Controller {
} }
else else
{ {
$this->setFlashMessage('Failed to add new manga to list' . $result['body'], 'error'); $this->setFlashMessage('Failed to add new manga to list:' . print_r($data, TRUE), 'error');
} }
$this->sessionRedirect(); $this->sessionRedirect();
@ -180,7 +178,7 @@ final class Manga extends Controller {
* @throws InvalidArgumentException * @throws InvalidArgumentException
* @return void * @return void
*/ */
public function edit($id, $status = 'All'): void public function edit(string $id, string $status = 'All'): void
{ {
$this->checkAuth(); $this->checkAuth();
@ -218,14 +216,12 @@ final class Manga extends Controller {
* *
* @return void * @return void
* @throws Throwable * @throws Throwable
* @throws NotFoundException
* @throws ContainerException
*/ */
public function formUpdate(): void public function formUpdate(): void
{ {
$this->checkAuth(); $this->checkAuth();
$data = $this->request->getParsedBody(); $data = (array)$this->request->getParsedBody();
// Do some minor data manipulation for // Do some minor data manipulation for
// large form-based updates // large form-based updates
@ -275,8 +271,6 @@ final class Manga extends Controller {
/** /**
* Remove an manga from the list * Remove an manga from the list
* *
* @throws ContainerException
* @throws NotFoundException
* @throws Throwable * @throws Throwable
* @return void * @return void
*/ */
@ -284,7 +278,7 @@ final class Manga extends Controller {
{ {
$this->checkAuth(); $this->checkAuth();
$body = $this->request->getParsedBody(); $body = (array)$this->request->getParsedBody();
$response = $this->model->deleteLibraryItem($body['id'], $body['mal_id']); $response = $this->model->deleteLibraryItem($body['id'], $body['mal_id']);
if ($response) if ($response)

View File

@ -74,7 +74,7 @@ final class Misc extends BaseController {
*/ */
public function loginAction(): void public function loginAction(): void
{ {
$post = $this->request->getParsedBody(); $post = (array)$this->request->getParsedBody();
if ($this->auth->authenticate($post['password'])) if ($this->auth->authenticate($post['password']))
{ {
@ -83,7 +83,11 @@ final class Misc extends BaseController {
} }
$this->setFlashMessage('Invalid username or password.'); $this->setFlashMessage('Invalid username or password.');
$this->redirect($this->url->generate('login'), 303);
$redirectUrl = $this->url->generate('login');
$redirectUrl = ($redirectUrl !== FALSE) ? $redirectUrl : '';
$this->redirect($redirectUrl, 303);
} }
/** /**

View File

@ -84,7 +84,7 @@ final class Settings extends BaseController {
*/ */
public function update(): void public function update(): void
{ {
$post = $this->request->getParsedBody(); $post = (array)$this->request->getParsedBody();
unset($post['settings-tabs']); unset($post['settings-tabs']);
$saved = $this->settingsModel->saveSettingsFile($post); $saved = $this->settingsModel->saveSettingsFile($post);
@ -93,7 +93,10 @@ final class Settings extends BaseController {
? $this->setFlashMessage('Saved config settings.', 'success') ? $this->setFlashMessage('Saved config settings.', 'success')
: $this->setFlashMessage('Failed to save config file.', 'error'); : $this->setFlashMessage('Failed to save config file.', 'error');
$this->redirect($this->url->generate('settings'), 303); $redirectUrl = $this->url->generate('settings');
$redirectUrl = ($redirectUrl !== FALSE) ? $redirectUrl : '';
$this->redirect($redirectUrl, 303);
} }
/** /**
@ -152,6 +155,9 @@ final class Settings extends BaseController {
? $this->setFlashMessage('Linked Anilist Account', 'success') ? $this->setFlashMessage('Linked Anilist Account', 'success')
: $this->setFlashMessage('Error Linking Anilist Account', 'error'); : $this->setFlashMessage('Error Linking Anilist Account', 'error');
$this->redirect($this->url->generate('settings'), 303); $redirectUrl = $this->url->generate('settings');
$redirectUrl = ($redirectUrl !== FALSE) ? $redirectUrl : '';
$this->redirect($redirectUrl, 303);
} }
} }

View File

@ -16,19 +16,23 @@
namespace Aviat\AnimeClient; namespace Aviat\AnimeClient;
use Aviat\AnimeClient\Enum\EventType; use Aviat\Ion\Json;
use function Aviat\Ion\_dir; use Aura\Router\{
Map,
use Aura\Router\{Map, Matcher, Route, Rule}; Matcher,
Route,
Rule,
};
use Aviat\AnimeClient\API\FailedResponseException; use Aviat\AnimeClient\API\FailedResponseException;
use Aviat\Ion\Di\ContainerInterface; use Aviat\Ion\Di\ContainerInterface;
use Aviat\Ion\Event;
use Aviat\Ion\Friend; use Aviat\Ion\Friend;
use Aviat\Ion\Type\StringType; use Aviat\Ion\Type\StringType;
use JetBrains\PhpStorm\ArrayShape;
use LogicException; use LogicException;
use ReflectionException; use ReflectionException;
use function Aviat\Ion\_dir;
/** /**
* Basic routing/ dispatch * Basic routing/ dispatch
*/ */
@ -78,7 +82,7 @@ final class Dispatcher extends RoutingBase {
* *
* @return Route|false * @return Route|false
*/ */
public function getRoute() public function getRoute(): Route | false
{ {
$logger = $this->container->getLogger(); $logger = $this->container->getLogger();
@ -132,7 +136,7 @@ final class Dispatcher extends RoutingBase {
{ {
// If not route was matched, return an appropriate http // If not route was matched, return an appropriate http
// error message // error message
$errorRoute = $this->getErrorParams(); $errorRoute = (array)$this->getErrorParams();
$controllerName = DEFAULT_CONTROLLER; $controllerName = DEFAULT_CONTROLLER;
$actionMethod = $errorRoute['action_method']; $actionMethod = $errorRoute['action_method'];
$params = $errorRoute['params']; $params = $errorRoute['params'];
@ -152,11 +156,11 @@ final class Dispatcher extends RoutingBase {
* Parse out the arguments for the appropriate controller for * Parse out the arguments for the appropriate controller for
* the current route * the current route
* *
* @param Route $route * @param Friend $route
* @throws LogicException * @throws LogicException
* @return array * @return array
*/ */
protected function processRoute($route): array protected function processRoute(Friend $route): array
{ {
if ( ! array_key_exists('controller', $route->attributes)) if ( ! array_key_exists('controller', $route->attributes))
{ {
@ -166,7 +170,7 @@ final class Dispatcher extends RoutingBase {
$controllerName = $route->attributes['controller']; $controllerName = $route->attributes['controller'];
// Get the full namespace for a controller if a short name is given // Get the full namespace for a controller if a short name is given
if (strpos($controllerName, '\\') === FALSE) if ( ! str_contains($controllerName, '\\'))
{ {
$map = $this->getControllerList(); $map = $this->getControllerList();
$controllerName = $map[$controllerName]; $controllerName = $map[$controllerName];
@ -191,7 +195,7 @@ final class Dispatcher extends RoutingBase {
$logger = $this->container->getLogger(); $logger = $this->container->getLogger();
if ($logger !== NULL) if ($logger !== NULL)
{ {
$logger->info(json_encode($params)); $logger->info(Json::encode($params));
} }
return [ return [
@ -244,6 +248,10 @@ final class Dispatcher extends RoutingBase {
$path = trim($path, '/'); $path = trim($path, '/');
$actualPath = realpath(_dir(SRC_DIR, $path)); $actualPath = realpath(_dir(SRC_DIR, $path));
$classFiles = glob("{$actualPath}/*.php"); $classFiles = glob("{$actualPath}/*.php");
if ($classFiles === FALSE)
{
return [];
}
$controllers = []; $controllers = [];
@ -282,9 +290,10 @@ final class Dispatcher extends RoutingBase {
$logger->debug('Dispatcher - controller arguments', $params); $logger->debug('Dispatcher - controller arguments', $params);
} }
call_user_func_array([$controller, $method], array_values($params)); $params = array_values($params);
$controller->$method(...$params);
} }
catch (FailedResponseException $e) catch (FailedResponseException)
{ {
$controllerName = DEFAULT_CONTROLLER; $controllerName = DEFAULT_CONTROLLER;
$controller = new $controllerName($this->container); $controller = new $controllerName($this->container);

View File

@ -48,7 +48,7 @@ final class FormGenerator {
* Create a new FormGenerator * Create a new FormGenerator
* *
* @param ContainerInterface $container * @param ContainerInterface $container
* @return $this * @return self
*/ */
public static function new(ContainerInterface $container): self public static function new(ContainerInterface $container): self
{ {

View File

@ -22,7 +22,7 @@ use Aviat\Ion\Di\ContainerInterface;
use Aviat\Ion\Exception\ConfigException; use Aviat\Ion\Exception\ConfigException;
use Aviat\Ion\Type\ArrayType; use Aviat\Ion\Type\ArrayType;
use Aviat\Ion\Type\StringType; use Aviat\Ion\Type\StringType;
use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ServerRequestInterface;
/** /**
* Helper object to manage menu creation and selection * Helper object to manage menu creation and selection
@ -39,9 +39,9 @@ final class MenuGenerator extends UrlGenerator {
/** /**
* Request object * Request object
* *
* @var RequestInterface * @var ServerRequestInterface
*/ */
protected RequestInterface $request; protected ServerRequestInterface $request;
/** /**
* @param ContainerInterface $container * @param ContainerInterface $container

View File

@ -85,7 +85,7 @@ trait MediaTrait {
* @param string $itemId * @param string $itemId
* @return AnimeListItem|MangaListItem * @return AnimeListItem|MangaListItem
*/ */
public function getLibraryItem(string $itemId) public function getLibraryItem(string $itemId): AnimeListItem|MangaListItem
{ {
return $this->kitsuModel->getListItem($itemId); return $this->kitsuModel->getListItem($itemId);
} }

View File

@ -22,7 +22,7 @@ use Aviat\Ion\Di\Exception\ContainerException;
use Aviat\Ion\Di\Exception\NotFoundException; use Aviat\Ion\Di\Exception\NotFoundException;
use Aviat\Ion\Exception\ConfigException; use Aviat\Ion\Exception\ConfigException;
use Aviat\Ion\Type\StringType; use Aviat\Ion\Type\StringType;
use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ServerRequestInterface;
/** /**
* Base for routing/url classes * Base for routing/url classes
@ -43,9 +43,9 @@ class RoutingBase {
/** /**
* Class wrapper for input superglobals * Class wrapper for input superglobals
* @var RequestInterface * @var ServerRequestInterface
*/ */
protected RequestInterface $request; protected ServerRequestInterface $request;
/** /**
* Constructor * Constructor
@ -64,8 +64,7 @@ class RoutingBase {
/** /**
* Get the current url path * Get the current url path
* @throws ContainerException *
* @throws NotFoundException
* @return string * @return string
*/ */
public function path(): string public function path(): string
@ -82,8 +81,7 @@ class RoutingBase {
/** /**
* Get the url segments * Get the url segments
* @throws ContainerException *
* @throws NotFoundException
* @return array * @return array
*/ */
public function segments(): array public function segments(): array
@ -96,11 +94,10 @@ class RoutingBase {
* Get a segment of the current url * Get a segment of the current url
* *
* @param int $num * @param int $num
* @throws ContainerException *
* @throws NotFoundException
* @return string|null * @return string|null
*/ */
public function getSegment($num): ?string public function getSegment(int $num): ?string
{ {
$segments = $this->segments(); $segments = $this->segments();
return $segments[$num] ?? NULL; return $segments[$num] ?? NULL;
@ -109,8 +106,6 @@ class RoutingBase {
/** /**
* Retrieve the last url segment * Retrieve the last url segment
* *
* @throws ContainerException
* @throws NotFoundException
* @return string * @return string
*/ */
public function lastSegment(): string public function lastSegment(): string

View File

@ -0,0 +1,32 @@
<?php declare(strict_types=1);
/**
* Hummingbird Anime List Client
*
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 8
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2021 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 5.2
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
namespace Aviat\Ion;
/**
* View Interface abstracting an HTTP Response
*/
interface HttpViewInterface extends ViewInterface {
/**
* Set the status code of the request
*
* @param int $code
* @throws \InvalidArgumentException
* @return self
*/
public function setStatusCode(int $code): self;
}

View File

@ -18,8 +18,6 @@ namespace Aviat\Ion\View;
use Aviat\Ion\Di\ContainerAware; use Aviat\Ion\Di\ContainerAware;
use Aviat\Ion\Di\ContainerInterface; use Aviat\Ion\Di\ContainerInterface;
use Aviat\Ion\Di\Exception\ContainerException;
use Aviat\Ion\Di\Exception\NotFoundException;
use Laminas\Diactoros\Response\HtmlResponse; use Laminas\Diactoros\Response\HtmlResponse;
use const EXTR_OVERWRITE; use const EXTR_OVERWRITE;
@ -40,8 +38,6 @@ class HtmlView extends HttpView {
* Create the Html View * Create the Html View
* *
* @param ContainerInterface $container * @param ContainerInterface $container
* @throws ContainerException
* @throws NotFoundException
*/ */
public function __construct(ContainerInterface $container) public function __construct(ContainerInterface $container)
{ {
@ -57,6 +53,7 @@ class HtmlView extends HttpView {
* @param string $path * @param string $path
* @param array $data * @param array $data
* @return string * @return string
* @throws \Throwable
*/ */
public function renderTemplate(string $path, array $data): string public function renderTemplate(string $path, array $data): string
{ {
@ -74,9 +71,7 @@ class HtmlView extends HttpView {
// Very basic html minify, that won't affect content between html tags // Very basic html minify, that won't affect content between html tags
$buffer = preg_replace('/>\s+</', '> <', $buffer); return preg_replace('/>\s+</', '> <', $buffer);
return $buffer;
} }
} }
// End of HtmlView.php // End of HtmlView.php

View File

@ -16,7 +16,7 @@
namespace Aviat\Ion\View; namespace Aviat\Ion\View;
use Aviat\Ion\ViewInterface; use Aviat\Ion\HttpViewInterface;
use Laminas\Diactoros\Response; use Laminas\Diactoros\Response;
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter; use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
@ -26,7 +26,7 @@ use Psr\Http\Message\ResponseInterface;
/** /**
* Base view class for Http output * Base view class for Http output
*/ */
class HttpView implements ViewInterface{ class HttpView implements HttpViewInterface{
/** /**
* HTTP response Object * HTTP response Object
@ -103,9 +103,9 @@ class HttpView implements ViewInterface{
* Set the output string * Set the output string
* *
* @param mixed $string * @param mixed $string
* @return ViewInterface * @return HttpViewInterface
*/ */
public function setOutput($string): ViewInterface public function setOutput($string): HttpViewInterface
{ {
$this->response->getBody()->write($string); $this->response->getBody()->write($string);
@ -117,9 +117,9 @@ class HttpView implements ViewInterface{
* Append additional output. * Append additional output.
* *
* @param string $string * @param string $string
* @return ViewInterface * @return HttpViewInterface
*/ */
public function appendOutput(string $string): ViewInterface public function appendOutput(string $string): HttpViewInterface
{ {
return $this->setOutput($string); return $this->setOutput($string);
} }

View File

@ -17,7 +17,7 @@
namespace Aviat\Ion\View; namespace Aviat\Ion\View;
use Aviat\Ion\Json; use Aviat\Ion\Json;
use Aviat\Ion\ViewInterface; use Aviat\Ion\HttpViewInterface;
/** /**
* View class to serialize Json * View class to serialize Json
@ -35,9 +35,9 @@ class JsonView extends HttpView {
* Set the output string * Set the output string
* *
* @param mixed $string * @param mixed $string
* @return ViewInterface * @return HttpViewInterface
*/ */
public function setOutput($string): ViewInterface public function setOutput(mixed $string): HttpViewInterface
{ {
if ( ! is_string($string)) if ( ! is_string($string))
{ {