mirror of
https://github.com/standardebooks/web.git
synced 2025-07-15 02:46:46 -04:00
Update PHPStan and Safe PHP, and review codebase for further type correctness
This commit is contained in:
parent
e2e14a3551
commit
9d1b66d19e
35 changed files with 301 additions and 169 deletions
|
@ -254,7 +254,7 @@ class Artwork{
|
|||
if(!isset($this->Dimensions)){
|
||||
$this->_Dimensions = '';
|
||||
try{
|
||||
list($imageWidth, $imageHeight) = getimagesize($this->ImageFsPath);
|
||||
list($imageWidth, $imageHeight) = (getimagesize($this->ImageFsPath) ?? throw new \Exception());
|
||||
if($imageWidth && $imageHeight){
|
||||
$this->_Dimensions = number_format($imageWidth) . ' × ' . number_format($imageHeight);
|
||||
}
|
||||
|
@ -529,9 +529,14 @@ class Artwork{
|
|||
}
|
||||
|
||||
// Check for minimum dimensions.
|
||||
list($imageWidth, $imageHeight) = getimagesize($imagePath);
|
||||
if(!$imageWidth || !$imageHeight || $imageWidth < ARTWORK_IMAGE_MINIMUM_WIDTH || $imageHeight < ARTWORK_IMAGE_MINIMUM_HEIGHT){
|
||||
$error->Add(new Exceptions\ArtworkImageDimensionsTooSmallException());
|
||||
try{
|
||||
list($imageWidth, $imageHeight) = (getimagesize($imagePath) ?? throw new \Exception());
|
||||
if(!$imageWidth || !$imageHeight || $imageWidth < ARTWORK_IMAGE_MINIMUM_WIDTH || $imageHeight < ARTWORK_IMAGE_MINIMUM_HEIGHT){
|
||||
$error->Add(new Exceptions\ArtworkImageDimensionsTooSmallException());
|
||||
}
|
||||
}
|
||||
catch(\Exception){
|
||||
$error->Add(new Exceptions\InvalidImageUploadException());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -647,7 +652,7 @@ class Artwork{
|
|||
}
|
||||
|
||||
preg_match('|^/books/edition/[^/]+/([^/]+)$|ius', $parsedUrl['path'], $matches);
|
||||
$id = $matches[1];
|
||||
$id = $matches[1] ?? '';
|
||||
|
||||
parse_str($parsedUrl['query'] ?? '', $vars);
|
||||
|
||||
|
|
|
@ -61,10 +61,8 @@ class AtomFeed extends Feed{
|
|||
}
|
||||
}
|
||||
else{
|
||||
if($entry->Updated !== null){
|
||||
$obj->Updated = $entry->Updated->format(Enums\DateTimeFormat::Iso->value);
|
||||
$obj->Id = $entry->Id;
|
||||
}
|
||||
$obj->Updated = $entry->Updated->format(Enums\DateTimeFormat::Iso->value);
|
||||
$obj->Id = $entry->Id;
|
||||
}
|
||||
|
||||
if(isset($obj->Id)){
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?
|
||||
// Auto-included by Composer in `composer.json` to satisfy PHPStan.
|
||||
use Safe\DateTimeImmutable;
|
||||
use function Safe\get_cfg_var;
|
||||
use function Safe\define;
|
||||
|
||||
const NOW = new DateTimeImmutable();
|
||||
|
|
|
@ -71,6 +71,7 @@ Db::Connect(DATABASE_DEFAULT_DATABASE, DATABASE_DEFAULT_HOST);
|
|||
Session::InitializeFromCookie();
|
||||
|
||||
if(Session::$User === null){
|
||||
/** @var ?string $httpBasicAuthLogin */
|
||||
$httpBasicAuthLogin = $_SERVER['PHP_AUTH_USER'] ?? null;
|
||||
|
||||
if($httpBasicAuthLogin !== null){
|
||||
|
@ -78,6 +79,7 @@ if(Session::$User === null){
|
|||
|
||||
$session = new Session();
|
||||
try{
|
||||
/** @var ?string $password */
|
||||
$password = $_SERVER['PHP_AUTH_PW'] ?? null;
|
||||
if($password == ''){
|
||||
$password = null;
|
||||
|
|
|
@ -465,7 +465,7 @@ final class Ebook{
|
|||
return $this->_HeroImageUrl;
|
||||
}
|
||||
|
||||
protected function GetHeroImageAvifUrl(): ?string{
|
||||
protected function GetHeroImageAvifUrl(): string{
|
||||
if(!isset($this->_HeroImageAvifUrl)){
|
||||
if(file_exists(WEB_ROOT . '/images/covers/' . $this->UrlSafeIdentifier . '-hero.avif')){
|
||||
$this->_HeroImageAvifUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . substr(sha1($this->Updated->format(Enums\DateTimeFormat::UnixTimestamp->value)), 0, 8) . '-hero.avif';
|
||||
|
@ -486,7 +486,7 @@ final class Ebook{
|
|||
return $this->_HeroImage2xUrl;
|
||||
}
|
||||
|
||||
protected function GetHeroImage2xAvifUrl(): ?string{
|
||||
protected function GetHeroImage2xAvifUrl(): string{
|
||||
if(!isset($this->_HeroImage2xAvifUrl)){
|
||||
if(file_exists(WEB_ROOT . '/images/covers/' . $this->UrlSafeIdentifier . '-hero@2x.avif')){
|
||||
$this->_HeroImage2xAvifUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . substr(sha1($this->Updated->format(Enums\DateTimeFormat::UnixTimestamp->value)), 0, 8) . '-hero@2x.avif';
|
||||
|
@ -507,7 +507,7 @@ final class Ebook{
|
|||
return $this->_CoverImageUrl;
|
||||
}
|
||||
|
||||
protected function GetCoverImageAvifUrl(): ?string{
|
||||
protected function GetCoverImageAvifUrl(): string{
|
||||
if(!isset($this->_CoverImageAvifUrl)){
|
||||
if(file_exists(WEB_ROOT . '/images/covers/' . $this->UrlSafeIdentifier . '-cover.avif')){
|
||||
$this->_CoverImageAvifUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . substr(sha1($this->Updated->format(Enums\DateTimeFormat::UnixTimestamp->value)), 0, 8) . '-cover.avif';
|
||||
|
@ -528,7 +528,7 @@ final class Ebook{
|
|||
return $this->_CoverImage2xUrl;
|
||||
}
|
||||
|
||||
protected function GetCoverImage2xAvifUrl(): ?string{
|
||||
protected function GetCoverImage2xAvifUrl(): string{
|
||||
if(!isset($this->_CoverImage2xAvifUrl)){
|
||||
if(file_exists(WEB_ROOT . '/images/covers/' . $this->UrlSafeIdentifier . '-cover@2x.avif')){
|
||||
$this->_CoverImage2xAvifUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . substr(sha1($this->Updated->format(Enums\DateTimeFormat::UnixTimestamp->value)), 0, 8) . '-cover@2x.avif';
|
||||
|
@ -839,7 +839,7 @@ final class Ebook{
|
|||
|
||||
// Fill in the short history of this repo.
|
||||
try{
|
||||
$historyEntries = explode("\n", shell_exec('cd ' . escapeshellarg($ebook->RepoFilesystemPath) . ' && git log -n5 --pretty=format:"%ct %H %s"'));
|
||||
$historyEntries = explode("\n", shell_exec('cd ' . escapeshellarg($ebook->RepoFilesystemPath) . ' && git log -n5 --pretty=format:"%ct %H %s"') ?? '');
|
||||
|
||||
$gitCommits = [];
|
||||
foreach($historyEntries as $logLine){
|
||||
|
@ -873,13 +873,13 @@ final class Ebook{
|
|||
$ebook->AlternateTitle = Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="dcterms:alternate"][@refines="#title"]'));
|
||||
|
||||
$date = $xml->xpath('/package/metadata/dc:date') ?: [];
|
||||
if($date !== false && sizeof($date) > 0){
|
||||
if(sizeof($date) > 0){
|
||||
/** @throws void */
|
||||
$ebook->EbookCreated = new DateTimeImmutable((string)$date[0]);
|
||||
}
|
||||
|
||||
$modifiedDate = $xml->xpath('/package/metadata/meta[@property="dcterms:modified"]') ?: [];
|
||||
if($modifiedDate !== false && sizeof($modifiedDate) > 0){
|
||||
if(sizeof($modifiedDate) > 0){
|
||||
/** @throws void */
|
||||
$ebook->EbookUpdated = new DateTimeImmutable((string)$modifiedDate[0]);
|
||||
}
|
||||
|
@ -949,7 +949,7 @@ final class Ebook{
|
|||
|
||||
$fileAs = null;
|
||||
$fileAsElement = $xml->xpath('/package/metadata/meta[@property="file-as"][@refines="#' . $id . '"]') ?: [];
|
||||
if($fileAsElement !== false && sizeof($fileAsElement) > 0){
|
||||
if(sizeof($fileAsElement) > 0){
|
||||
$fileAs = (string)$fileAsElement[0];
|
||||
}
|
||||
else{
|
||||
|
@ -1030,15 +1030,15 @@ final class Ebook{
|
|||
$ebook->Language = Ebook::NullIfEmpty($xml->xpath('/package/metadata/dc:language')) ?? '';
|
||||
|
||||
$wordCount = 0;
|
||||
$wordCountElement = $xml->xpath('/package/metadata/meta[@property="se:word-count"]');
|
||||
if($wordCountElement !== false && sizeof($wordCountElement) > 0){
|
||||
$wordCountElement = $xml->xpath('/package/metadata/meta[@property="se:word-count"]') ?: [];
|
||||
if(sizeof($wordCountElement) > 0){
|
||||
$wordCount = (int)$wordCountElement[0];
|
||||
}
|
||||
$ebook->WordCount = $wordCount;
|
||||
|
||||
$readingEase = 0;
|
||||
$readingEaseElement = $xml->xpath('/package/metadata/meta[@property="se:reading-ease.flesch"]');
|
||||
if($readingEaseElement !== false && sizeof($readingEaseElement) > 0){
|
||||
$readingEaseElement = $xml->xpath('/package/metadata/meta[@property="se:reading-ease.flesch"]') ?: [];
|
||||
if(sizeof($readingEaseElement) > 0){
|
||||
$readingEase = (float)$readingEaseElement[0];
|
||||
}
|
||||
$ebook->ReadingEase = $readingEase;
|
||||
|
@ -1124,7 +1124,7 @@ final class Ebook{
|
|||
*/
|
||||
protected static function MatchContributorUrlNameToIdentifier(string $urlName, string $identifier): string{
|
||||
if(preg_match('|' . $urlName . '[^\/_]*|ius', $identifier, $matches)){
|
||||
return $matches[0];
|
||||
return $matches[0] ?? '';
|
||||
}
|
||||
else{
|
||||
return $urlName;
|
||||
|
|
|
@ -45,7 +45,7 @@ class Email{
|
|||
$phpMailer->AddAddress($this->To, $this->ToName);
|
||||
$phpMailer->Subject = $this->Subject;
|
||||
$phpMailer->CharSet = 'UTF-8';
|
||||
if($this->TextBody !== null && $this->TextBody != ''){
|
||||
if($this->TextBody != ''){
|
||||
$phpMailer->IsHTML(true);
|
||||
$phpMailer->Body = $this->Body;
|
||||
$phpMailer->AltBody = $this->TextBody;
|
||||
|
@ -55,9 +55,7 @@ class Email{
|
|||
}
|
||||
|
||||
foreach($this->Attachments as $attachment){
|
||||
if(is_array($attachment)){
|
||||
$phpMailer->addStringAttachment($attachment['contents'], $attachment['filename']);
|
||||
}
|
||||
$phpMailer->addStringAttachment($attachment['contents'], $attachment['filename']);
|
||||
}
|
||||
|
||||
$phpMailer->IsSMTP();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?
|
||||
use Safe\DateTimeImmutable;
|
||||
|
||||
use function Safe\file_get_contents;
|
||||
use function Safe\ini_get;
|
||||
use function Safe\glob;
|
||||
use function Safe\preg_match;
|
||||
|
@ -8,6 +9,31 @@ use function Safe\preg_replace;
|
|||
use function Safe\mb_convert_encoding;
|
||||
|
||||
class HttpInput{
|
||||
/**
|
||||
* If we received a `DELETE`, `PATCH`, or `PUT` request, parse the request body into `$_POST`.
|
||||
*
|
||||
* This can't handle file uploads, which due to PHP limitations *must* be sent via `POST`.
|
||||
*/
|
||||
public static function Initialize(): void{
|
||||
/** @var string $contentType */
|
||||
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
|
||||
if(
|
||||
isset($_SERVER['REQUEST_METHOD'])
|
||||
&&
|
||||
(
|
||||
$_SERVER['REQUEST_METHOD'] == Enums\HttpMethod::Delete->value
|
||||
||
|
||||
$_SERVER['REQUEST_METHOD'] == Enums\HttpMethod::Patch->value
|
||||
||
|
||||
$_SERVER['REQUEST_METHOD'] == Enums\HttpMethod::Put->value
|
||||
)
|
||||
&&
|
||||
preg_match('/^application\/x-www-form-urlencoded(;|$)/', $contentType)
|
||||
){
|
||||
parse_str(file_get_contents('php://input'), $_POST);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the HTTP method of the request, then include `<METHOD>.php` and exit.
|
||||
*/
|
||||
|
@ -22,10 +48,11 @@ class HttpInput{
|
|||
}
|
||||
|
||||
if($httpMethod == Enums\HttpMethod::Post){
|
||||
// If we're a HTTP POST, then we got here from a POST request initially, so just continue.
|
||||
// If we're a HTTP `POST`, then we got here from a `POST` request initially, so just continue.
|
||||
return;
|
||||
}
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
include($filename);
|
||||
|
||||
exit();
|
||||
|
@ -47,11 +74,13 @@ class HttpInput{
|
|||
*
|
||||
* @param ?array<Enums\HttpMethod> $allowedHttpMethods An array containing a list of allowed HTTP methods, or null if any valid HTTP method is allowed.
|
||||
* @param bool $throwException If the request HTTP method isn't allowed, then throw an exception; otherwise, output HTTP 405 and exit the script immediately.
|
||||
* @throws Exceptions\HttpMethodNotAllowedException If the HTTP method is not allowed, and `$throwException` is `true`.
|
||||
* @throws Exceptions\HttpMethodNotAllowedException If the HTTP method is recognized but not allowed, and `$throwException` is `true`.
|
||||
*/
|
||||
public static function ValidateRequestMethod(?array $allowedHttpMethods = null, bool $throwException = false): Enums\HttpMethod{
|
||||
try{
|
||||
$requestMethod = Enums\HttpMethod::from($_POST['_method'] ?? $_GET['_method'] ?? $_SERVER['REQUEST_METHOD']);
|
||||
/** @var string $requestMethodString */
|
||||
$requestMethodString = $_POST['_method'] ?? $_GET['_method'] ?? $_SERVER['REQUEST_METHOD'];
|
||||
$requestMethod = Enums\HttpMethod::from($requestMethodString);
|
||||
if($allowedHttpMethods !== null){
|
||||
$isRequestMethodAllowed = false;
|
||||
foreach($allowedHttpMethods as $allowedHttpMethod){
|
||||
|
@ -65,9 +94,14 @@ class HttpInput{
|
|||
}
|
||||
}
|
||||
}
|
||||
catch(\ValueError | Exceptions\HttpMethodNotAllowedException){
|
||||
catch(\ValueError | Exceptions\HttpMethodNotAllowedException $ex){
|
||||
if($throwException){
|
||||
throw new Exceptions\HttpMethodNotAllowedException();
|
||||
if($ex instanceof \ValueError){
|
||||
throw new Exceptions\HttpMethodNotAllowedException();
|
||||
}
|
||||
else{
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if($allowedHttpMethods !== null){
|
||||
|
@ -82,7 +116,7 @@ class HttpInput{
|
|||
}
|
||||
|
||||
/**
|
||||
* @return int The maximum size for an HTTP POST request, in bytes.
|
||||
* @return int The maximum size for an HTTP `POST` request, in bytes.
|
||||
*/
|
||||
public static function GetMaxPostSize(): int{
|
||||
$post_max_size = ini_get('upload_max_filesize');
|
||||
|
@ -106,6 +140,7 @@ class HttpInput{
|
|||
elseif(sizeof($_FILES) > 0){
|
||||
// We received files but may have an error because the size exceeded our limit.
|
||||
foreach($_FILES as $file){
|
||||
/** @var array<string, int> $file */
|
||||
$error = $file['error'] ?? UPLOAD_ERR_OK;
|
||||
|
||||
if($error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE){
|
||||
|
@ -118,7 +153,9 @@ class HttpInput{
|
|||
}
|
||||
|
||||
public static function GetRequestType(): Enums\HttpRequestType{
|
||||
return preg_match('/\btext\/html\b/ius', $_SERVER['HTTP_ACCEPT'] ?? '') ? Enums\HttpRequestType::Web : Enums\HttpRequestType::Rest;
|
||||
/** @var string $httpAccept */
|
||||
$httpAccept = $_SERVER['HTTP_ACCEPT'] ?? '';
|
||||
return preg_match('/\btext\/html\b/ius',$httpAccept) ? Enums\HttpRequestType::Web : Enums\HttpRequestType::Rest;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,7 +218,7 @@ class HttpInput{
|
|||
|
||||
$object = $_SESSION[$variable] ?? null;
|
||||
|
||||
if($object !== null){
|
||||
if(is_object($object)){
|
||||
foreach($class as $c){
|
||||
if(is_a($object, $c)){
|
||||
return $object;
|
||||
|
@ -200,12 +237,17 @@ class HttpInput{
|
|||
public static function File(string $variable): ?string{
|
||||
$filePath = null;
|
||||
|
||||
if(isset($_FILES[$variable]) && $_FILES[$variable]['size'] > 0){
|
||||
if(!is_uploaded_file($_FILES[$variable]['tmp_name']) || $_FILES[$variable]['error'] > UPLOAD_ERR_OK){
|
||||
throw new Exceptions\InvalidFileUploadException();
|
||||
}
|
||||
if(isset($_FILES[$variable])){
|
||||
/** @var array{'error': int, 'size': int, 'tmp_name': string} $file */
|
||||
$file = $_FILES[$variable];
|
||||
|
||||
$filePath = $_FILES[$variable]['tmp_name'] ?? null;
|
||||
if($file['size'] > 0){
|
||||
if(!is_uploaded_file($file['tmp_name']) || $file['error'] > UPLOAD_ERR_OK){
|
||||
throw new Exceptions\InvalidFileUploadException();
|
||||
}
|
||||
|
||||
$filePath = $file['tmp_name'];
|
||||
}
|
||||
}
|
||||
|
||||
return $filePath;
|
||||
|
@ -221,11 +263,9 @@ class HttpInput{
|
|||
}
|
||||
|
||||
/**
|
||||
* @return array<string>|array<int>|array<float>|array<bool>|string|int|float|bool|DateTimeImmutable|null
|
||||
* @return array<string>|array<int>|array<float>|array<bool>|array<mixed>|string|int|float|bool|DateTimeImmutable|null
|
||||
*/
|
||||
private static function GetHttpVar(string $variable, Enums\HttpVariableType $type, Enums\HttpVariableSource $set): mixed{
|
||||
// Note that in `Core.php` we parse the request body of DELETE, PATCH, and PUT into `$_POST`.
|
||||
|
||||
$vars = [];
|
||||
|
||||
switch($set){
|
||||
|
@ -262,6 +302,10 @@ class HttpInput{
|
|||
|
||||
switch($type){
|
||||
case Enums\HttpVariableType::String:
|
||||
if(!is_string($var)){
|
||||
return '';
|
||||
}
|
||||
|
||||
// Attempt to fix broken UTF8 strings, often passed by bots and scripts.
|
||||
// Broken UTF8 can cause exceptions in functions like `preg_replace()`.
|
||||
try{
|
||||
|
@ -282,7 +326,7 @@ class HttpInput{
|
|||
}
|
||||
break;
|
||||
case Enums\HttpVariableType::Boolean:
|
||||
if($var === false || $var === '0' || strtolower($var) == 'false' || strtolower($var) == 'off'){
|
||||
if($var === false || (is_string($var) && ($var === '0' || strtolower($var) == 'false' || strtolower($var) == 'off'))){
|
||||
return false;
|
||||
}
|
||||
else{
|
||||
|
@ -299,7 +343,7 @@ class HttpInput{
|
|||
}
|
||||
break;
|
||||
case Enums\HttpVariableType::DateTime:
|
||||
if($var != ''){
|
||||
if(is_string($var) && $var != ''){
|
||||
try{
|
||||
return new DateTimeImmutable($var);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ class Image{
|
|||
}
|
||||
|
||||
/**
|
||||
* @return resource
|
||||
* @return \GdImage
|
||||
* @throws \Safe\Exceptions\ImageException
|
||||
* @throws Exceptions\InvalidImageUploadException
|
||||
*/
|
||||
|
@ -43,7 +43,7 @@ class Image{
|
|||
}
|
||||
|
||||
/**
|
||||
* @return resource
|
||||
* @return \GdImage
|
||||
* @throws Exceptions\InvalidImageUploadException
|
||||
*/
|
||||
private function GetImageHandleFromTiff(){
|
||||
|
@ -98,8 +98,8 @@ class Image{
|
|||
throw new Exceptions\InvalidImageUploadException($ex->getMessage());
|
||||
}
|
||||
|
||||
$imageWidth = $imageDimensions[0];
|
||||
$imageHeight = $imageDimensions[1];
|
||||
$imageWidth = $imageDimensions[0] ?? 0;
|
||||
$imageHeight = $imageDimensions[1] ?? 0;
|
||||
|
||||
if($imageHeight > $imageWidth){
|
||||
$destinationHeight = $height;
|
||||
|
|
|
@ -15,7 +15,9 @@ class Manual{
|
|||
|
||||
public static function GetRequestedVersion(): ?string{
|
||||
try{
|
||||
if(preg_match_all('|/manual/([0-9]+\.[0-9]+\.[0-9]+)|ius', $_SERVER['REQUEST_URI'], $matches)){
|
||||
/** @var string $requestUri */
|
||||
$requestUri = $_SERVER['REQUEST_URI'];
|
||||
if(preg_match_all('|/manual/([0-9]+\.[0-9]+\.[0-9]+)|ius', $requestUri, $matches)){
|
||||
return($matches[1][0]);
|
||||
}
|
||||
else{
|
||||
|
|
|
@ -36,6 +36,9 @@ class Museum{
|
|||
|
||||
$parsedUrl['path'] = $parsedUrl['path'] ?? '';
|
||||
|
||||
// We've initialized `$parsedUrl`, formally define it for PHPStan.
|
||||
/** @var array{'path': string, 'host': string, 'fragment'?: string, 'query'?: string} $parsedUrl */
|
||||
|
||||
// We can't match on TLD because extracting the TLD for double-barrel TLDs, like .gov.uk, requires a whitelist.
|
||||
|
||||
if(preg_match('/\brijksmuseum\.nl$/ius', $parsedUrl['host'])){
|
||||
|
@ -112,10 +115,6 @@ class Museum{
|
|||
throw new Exceptions\InvalidMuseumUrlException($url, $exampleUrl);
|
||||
}
|
||||
|
||||
if($parsedUrl['path'] != '/eMP/eMuseumPlus'){
|
||||
throw new Exceptions\InvalidMuseumUrlException($url, $exampleUrl);
|
||||
}
|
||||
|
||||
parse_str($parsedUrl['query'] ?? '', $vars);
|
||||
|
||||
if(!isset($vars['objectId']) || is_array($vars['objectId'])){
|
||||
|
|
|
@ -15,8 +15,8 @@ class Poll{
|
|||
public string $UrlName;
|
||||
public string $Description;
|
||||
public DateTimeImmutable $Created;
|
||||
public DateTimeImmutable $Start;
|
||||
public DateTimeImmutable $End;
|
||||
public ?DateTimeImmutable $Start;
|
||||
public ?DateTimeImmutable $End;
|
||||
|
||||
protected string $_Url;
|
||||
/** @var array<PollItem> $_PollItems */
|
||||
|
|
|
@ -9,7 +9,7 @@ class PollItem{
|
|||
public int $PollItemId;
|
||||
public int $PollId;
|
||||
public string $Name;
|
||||
public string $Description;
|
||||
public ?string $Description;
|
||||
|
||||
protected int $_VoteCount;
|
||||
protected Poll $_Poll;
|
||||
|
|
|
@ -68,6 +68,7 @@ class Template{
|
|||
public static function RedirectToLogin(bool $redirectToDestination = true, ?string $destinationUrl = null): void{
|
||||
if($redirectToDestination){
|
||||
if($destinationUrl === null){
|
||||
/** @var string $destinationUrl */
|
||||
$destinationUrl = $_SERVER['SCRIPT_URL'];
|
||||
}
|
||||
|
||||
|
@ -81,6 +82,8 @@ class Template{
|
|||
}
|
||||
|
||||
public static function IsEreaderBrowser(): bool{
|
||||
return isset($_SERVER['HTTP_USER_AGENT']) && (strpos($_SERVER['HTTP_USER_AGENT'], "Kobo") !== false || strpos($_SERVER['HTTP_USER_AGENT'], "Kindle") !== false);
|
||||
/** @var string $httpUserAgent */
|
||||
$httpUserAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
return $httpUserAgent != '' && (strpos($httpUserAgent, "Kobo") !== false || strpos($httpUserAgent, "Kindle") !== false);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue