Bump PHPStan check level to max and add final round of type hints

This commit is contained in:
Alex Cabal 2024-05-13 10:48:05 -05:00
parent 110c091a7b
commit 70ae877dd8
15 changed files with 86 additions and 52 deletions

View file

@ -8,7 +8,7 @@ parameters:
# Ignore errors caused by Template static class reflection # Ignore errors caused by Template static class reflection
- '#Call to an undefined static method Template::[a-zA-Z0-9\\_]+\(\)\.#' - '#Call to an undefined static method Template::[a-zA-Z0-9\\_]+\(\)\.#'
level: level:
8 9
paths: paths:
- %rootDir%/../../../lib - %rootDir%/../../../lib
- %rootDir%/../../../www - %rootDir%/../../../www

View file

@ -9,21 +9,22 @@ class Db{
} }
/** /**
* @param string $query * @template T
* @param array<mixed> $args * @param string $query
* @param string $class * @param array<mixed> $args
* @return Array<mixed> * @param class-string<T> $class
*/ * @return Array<T>
*/
public static function Query(string $query, array $args = [], string $class = 'stdClass'): array{ public static function Query(string $query, array $args = [], string $class = 'stdClass'): array{
return $GLOBALS['DbConnection']->Query($query, $args, $class); return $GLOBALS['DbConnection']->Query($query, $args, $class);
} }
/** /**
* Returns a single integer value for the first column database query result. * Returns a single integer value for the first column database query result.
* This is useful for queries that return a single integer as a result, like count(*) or sum(*). * This is useful for queries that return a single integer as a result, like count(*) or sum(*).
* @param string $query * @param string $query
* @param array<mixed> $args * @param array<mixed> $args
*/ */
public static function QueryInt(string $query, array $args = []): int{ public static function QueryInt(string $query, array $args = []): int{
$result = $GLOBALS['DbConnection']->Query($query, $args); $result = $GLOBALS['DbConnection']->Query($query, $args);

View file

@ -41,24 +41,26 @@ class DbConnection{
$connectionString .= ';dbname=' . $defaultDatabase; $connectionString .= ';dbname=' . $defaultDatabase;
} }
// We can't use persistent connections (connection pooling) because we would have race condition problems with last_insert_id()
$params = [\PDO::ATTR_EMULATE_PREPARES => false, \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_PERSISTENT => false]; $params = [\PDO::ATTR_EMULATE_PREPARES => false, \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_PERSISTENT => false];
if($forceUtf8){ if($forceUtf8){
$params[\PDO::MYSQL_ATTR_INIT_COMMAND] = 'set names utf8mb4 collate utf8mb4_unicode_ci;'; $params[\PDO::MYSQL_ATTR_INIT_COMMAND] = 'set names utf8mb4 collate utf8mb4_unicode_ci;';
} }
// We can't use persistent connections (connection pooling) because we would have race condition problems with last_insert_id()
$this->_link = new \PDO($connectionString, $user, $password, $params); $this->_link = new \PDO($connectionString, $user, $password, $params);
} }
/** /**
* @param string $sql The SQL query to execute. * Execute a generic query in the database.
* @param array<mixed> $params An array of parameters to bind to the SQL statement. * @template T
* @param string $class The type of object to return in the return array. * @param string $sql The SQL query to execute.
* @return Array<mixed> * @param array<mixed> $params An array of parameters to bind to the SQL statement.
* @throws Exceptions\DuplicateDatabaseKeyException When a unique key constraint has been violated. * @param class-string<T> $class The type of object to return in the return array.
* @throws Exceptions\DatabaseQueryException When an error occurs during execution of the query. * @return Array<T>
*/ * @throws Exceptions\DuplicateDatabaseKeyException When a unique key constraint has been violated.
* @throws Exceptions\DatabaseQueryException When an error occurs during execution of the query.
*/
public function Query(string $sql, array $params = [], string $class = 'stdClass'): array{ public function Query(string $sql, array $params = [], string $class = 'stdClass'): array{
if($this->_link === null){ if($this->_link === null){
return []; return [];
@ -131,19 +133,11 @@ class DbConnection{
} }
/** /**
* @param \PDOException $ex The exception to create details from. * @template T
* @param string $sql The prepared SQL that caused the exception. * @param class-string<T> $class
* @param array<mixed> $params The parameters passed to the prepared SQL. * @return Array<T>
*/ * @throws \PDOException When an error occurs during execution of the query.
private function CreateDetailedException(\PDOException $ex, string $sql, array $params): Exceptions\DatabaseQueryException{ */
// Throw a custom exception that includes more information on the query and paramaters
return new Exceptions\DatabaseQueryException('Error when executing query: ' . $ex->getMessage() . '. Query: ' . $sql . '. Parameters: ' . vds($params));
}
/**
* @return Array<mixed>
* @throws \PDOException When an error occurs during execution of the query.
*/
private function ExecuteQuery(\PDOStatement $handle, string $class = 'stdClass'): array{ private function ExecuteQuery(\PDOStatement $handle, string $class = 'stdClass'): array{
$handle->execute(); $handle->execute();
@ -287,4 +281,14 @@ class DbConnection{
return $id; return $id;
} }
/**
* @param \PDOException $ex The exception to create details from.
* @param string $sql The prepared SQL that caused the exception.
* @param array<mixed> $params The parameters passed to the prepared SQL.
*/
private function CreateDetailedException(\PDOException $ex, string $sql, array $params): Exceptions\DatabaseQueryException{
// Throw a custom exception that includes more information on the query and paramaters
return new Exceptions\DatabaseQueryException('Error when executing query: ' . $ex->getMessage() . '. Query: ' . $sql . '. Parameters: ' . vds($params));
}
} }

View file

@ -15,7 +15,7 @@ class Ebook{
public string $WwwFilesystemPath; public string $WwwFilesystemPath;
public string $RepoFilesystemPath; public string $RepoFilesystemPath;
public string $Url; public string $Url;
public string $KindleCoverUrl; public ?string $KindleCoverUrl;
public string $EpubUrl; public string $EpubUrl;
public string $AdvancedEpubUrl; public string $AdvancedEpubUrl;
public string $KepubUrl; public string $KepubUrl;
@ -68,8 +68,8 @@ class Ebook{
public string $TitleWithCreditsHtml = ''; public string $TitleWithCreditsHtml = '';
public DateTimeImmutable $Created; public DateTimeImmutable $Created;
public DateTimeImmutable $Updated; public DateTimeImmutable $Updated;
public string $TextUrl; public ?string $TextUrl;
public string $TextSinglePageUrl; public ?string $TextSinglePageUrl;
public ?string $TextSinglePageSizeNumber = null; public ?string $TextSinglePageSizeNumber = null;
public ?string $TextSinglePageSizeUnit = null; public ?string $TextSinglePageSizeUnit = null;
/** @var ?array<string> $TocEntries */ /** @var ?array<string> $TocEntries */

View file

@ -84,18 +84,22 @@ class HttpInput{
return null; return null;
} }
/** @var ?string $var */
return $var; return $var;
} }
public static function Int(HttpVariableSource $set, string $variable): ?int{ public static function Int(HttpVariableSource $set, string $variable): ?int{
/** @var ?int */
return self::GetHttpVar($variable, HttpVariableType::Integer, $set); return self::GetHttpVar($variable, HttpVariableType::Integer, $set);
} }
public static function Bool(HttpVariableSource $set, string $variable): ?bool{ public static function Bool(HttpVariableSource $set, string $variable): ?bool{
/** @var ?bool */
return self::GetHttpVar($variable, HttpVariableType::Boolean, $set); return self::GetHttpVar($variable, HttpVariableType::Boolean, $set);
} }
public static function Dec(HttpVariableSource $set, string $variable): ?float{ public static function Dec(HttpVariableSource $set, string $variable): ?float{
/** @var ?float */
return self::GetHttpVar($variable, HttpVariableType::Decimal, $set); return self::GetHttpVar($variable, HttpVariableType::Decimal, $set);
} }
@ -104,9 +108,13 @@ class HttpInput{
* @return array<string> * @return array<string>
*/ */
public static function Array(HttpVariableSource $set, string $variable): ?array{ public static function Array(HttpVariableSource $set, string $variable): ?array{
/** @var array<string> */
return self::GetHttpVar($variable, HttpVariableType::Array, $set); return self::GetHttpVar($variable, HttpVariableType::Array, $set);
} }
/**
* @return array<string>|array<int>|array<float>|array<bool>|string|int|float|bool|null
*/
private static function GetHttpVar(string $variable, HttpVariableType $type, HttpVariableSource $set): mixed{ private static function GetHttpVar(string $variable, HttpVariableType $type, HttpVariableSource $set): mixed{
$vars = []; $vars = [];

View file

@ -113,6 +113,7 @@ class Library{
*/ */
public static function GetEbooks(): array{ public static function GetEbooks(): array{
// Get all ebooks, unsorted. // Get all ebooks, unsorted.
/** @var array<Ebook> */
return self::GetFromApcu('ebooks'); return self::GetFromApcu('ebooks');
} }
@ -121,6 +122,7 @@ class Library{
* @throws Exceptions\AppException * @throws Exceptions\AppException
*/ */
public static function GetEbooksByAuthor(string $wwwFilesystemPath): array{ public static function GetEbooksByAuthor(string $wwwFilesystemPath): array{
/** @var array<Ebook> */
return self::GetFromApcu('author-' . $wwwFilesystemPath); return self::GetFromApcu('author-' . $wwwFilesystemPath);
} }
@ -129,6 +131,7 @@ class Library{
*/ */
public static function GetEbooksByTag(string $tag): array{ public static function GetEbooksByTag(string $tag): array{
try{ try{
/** @var array<Ebook> */
return apcu_fetch('tag-' . $tag) ?? []; return apcu_fetch('tag-' . $tag) ?? [];
} }
catch(Safe\Exceptions\ApcuException){ catch(Safe\Exceptions\ApcuException){
@ -141,6 +144,7 @@ class Library{
* @throws Exceptions\AppException * @throws Exceptions\AppException
*/ */
public static function GetEbookCollections(): array{ public static function GetEbookCollections(): array{
/** @var array<string, Collection> */
return self::GetFromApcu('collections'); return self::GetFromApcu('collections');
} }
@ -150,6 +154,7 @@ class Library{
*/ */
public static function GetEbooksByCollection(string $collection): array{ public static function GetEbooksByCollection(string $collection): array{
// Do we have the tag's ebooks cached? // Do we have the tag's ebooks cached?
/** @var array<Ebook> */
return self::GetFromApcu('collection-' . $collection); return self::GetFromApcu('collection-' . $collection);
} }
@ -158,6 +163,7 @@ class Library{
* @throws Exceptions\AppException * @throws Exceptions\AppException
*/ */
public static function GetTags(): array{ public static function GetTags(): array{
/** @var array<Tag> */
return self::GetFromApcu('tags'); return self::GetFromApcu('tags');
} }
@ -538,7 +544,7 @@ class Library{
} }
/** /**
* @return array<string, array<int|string, array<int|string, mixed>>> * @return array<string, array<int|string, array<int|string, stdClass>>>
* @throws Exceptions\AppException * @throws Exceptions\AppException
*/ */
public static function RebuildBulkDownloadsCache(): array{ public static function RebuildBulkDownloadsCache(): array{
@ -662,6 +668,7 @@ class Library{
return null; return null;
} }
/** @var array<Ebook> $result */
$result = self::GetFromApcu('ebook-' . $ebookWwwFilesystemPath); $result = self::GetFromApcu('ebook-' . $ebookWwwFilesystemPath);
if(sizeof($result) > 0){ if(sizeof($result) > 0){

View file

@ -25,7 +25,7 @@ class Template{
* @param array<mixed> $arguments * @param array<mixed> $arguments
*/ */
public static function __callStatic(string $function, array $arguments): string{ public static function __callStatic(string $function, array $arguments): string{
if(isset($arguments[0])){ if(isset($arguments[0]) && is_array($arguments[0])){
return self::Get($function, $arguments[0]); return self::Get($function, $arguments[0]);
} }
else{ else{

View file

@ -16,10 +16,12 @@ if($GLOBALS['User'] !== null && $GLOBALS['User']->Benefits->CanBulkDownload){
$collection = []; $collection = [];
try{ try{
/** @var array<string, array<string, stdClass>> $collection */
$collection = apcu_fetch('bulk-downloads-' . $class); $collection = apcu_fetch('bulk-downloads-' . $class);
} }
catch(Safe\Exceptions\ApcuException){ catch(Safe\Exceptions\ApcuException){
$result = Library::RebuildBulkDownloadsCache(); $result = Library::RebuildBulkDownloadsCache();
/** @var array<string, array<string, stdClass>> $collection */
$collection = $result[$class]; $collection = $result[$class];
} }

View file

@ -17,10 +17,13 @@ try{
// Get all collections and then find the specific one we're looking for // Get all collections and then find the specific one we're looking for
try{ try{
/** @var array<stdClass> $collections */
$collections = apcu_fetch('bulk-downloads-collections'); $collections = apcu_fetch('bulk-downloads-collections');
} }
catch(Safe\Exceptions\ApcuException){ catch(Safe\Exceptions\ApcuException){
$result = Library::RebuildBulkDownloadsCache(); $result = Library::RebuildBulkDownloadsCache();
/** @var array<stdClass> $collections */
$collections = $result['collections']; $collections = $result['collections'];
} }
@ -41,10 +44,13 @@ try{
// Get all authors and then find the specific one we're looking for // Get all authors and then find the specific one we're looking for
try{ try{
/** @var array<stdClass> $collections */
$collections = apcu_fetch('bulk-downloads-authors'); $collections = apcu_fetch('bulk-downloads-authors');
} }
catch(Safe\Exceptions\ApcuException){ catch(Safe\Exceptions\ApcuException){
$result = Library::RebuildBulkDownloadsCache(); $result = Library::RebuildBulkDownloadsCache();
/** @var array<stdClass> $collections */
$collections = $result['authors']; $collections = $result['authors'];
} }
@ -70,7 +76,7 @@ catch(Exceptions\CollectionNotFoundException){
?><?= Template::Header(['title' => 'Download ', 'highlight' => '', 'description' => 'Download zip files containing all of the Standard Ebooks released in a given month.']) ?> ?><?= Template::Header(['title' => 'Download ', 'highlight' => '', 'description' => 'Download zip files containing all of the Standard Ebooks released in a given month.']) ?>
<main> <main>
<section class="bulk-downloads"> <section class="bulk-downloads">
<h1>Download the <?= $collection->Label ?> Collection</h1> <h1>Download the <?= $collection?->Label ?> Collection</h1>
<? if($canDownload){ ?> <? if($canDownload){ ?>
<p>Select the ebook format in which youd like to download this collection.</p> <p>Select the ebook format in which youd like to download this collection.</p>
<p>You can also read about <a href="/help/how-to-use-our-ebooks#which-file-to-download">which ebook format to download</a>.</p> <p>You can also read about <a href="/help/how-to-use-our-ebooks#which-file-to-download">which ebook format to download</a>.</p>

View file

@ -16,16 +16,13 @@ try{
// Do we have the ebook cached? // Do we have the ebook cached?
try{ try{
/** @var Ebook $ebook */
$ebook = apcu_fetch('ebook-' . $wwwFilesystemPath); $ebook = apcu_fetch('ebook-' . $wwwFilesystemPath);
} }
catch(Safe\Exceptions\ApcuException){ catch(Safe\Exceptions\ApcuException){
$ebook = new Ebook($wwwFilesystemPath); $ebook = new Ebook($wwwFilesystemPath);
} }
if($ebook === null){
throw new Exceptions\InvalidFileException();
}
switch($format){ switch($format){
case EbookFormat::Kepub: case EbookFormat::Kepub:
$downloadUrl = $ebook->KepubUrl; $downloadUrl = $ebook->KepubUrl;

View file

@ -28,6 +28,7 @@ try{
// https://standardebooks.org/ebooks/omar-khayyam/the-rubaiyat-of-omar-khayyam/edward-fitzgerald/edmund-dulac // https://standardebooks.org/ebooks/omar-khayyam/the-rubaiyat-of-omar-khayyam/edward-fitzgerald/edmund-dulac
// We can tell because if so, the dir we are passed will exist, but there will be no 'src' folder. // We can tell because if so, the dir we are passed will exist, but there will be no 'src' folder.
if(is_dir($wwwFilesystemPath) && !is_dir($wwwFilesystemPath . '/src')){ if(is_dir($wwwFilesystemPath) && !is_dir($wwwFilesystemPath . '/src')){
/** @var DirectoryIterator $file */
foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($wwwFilesystemPath)) as $file){ foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($wwwFilesystemPath)) as $file){
// This iterator will do a deep scan on the directory. When we hit another directory, the filename will be "." and the path will contain the directory path. // This iterator will do a deep scan on the directory. When we hit another directory, the filename will be "." and the path will contain the directory path.
// We want to find where the "src" directory is, and the directory directly below that will be the final web URL we're looking for. // We want to find where the "src" directory is, and the directory directly below that will be the final web URL we're looking for.
@ -39,6 +40,7 @@ try{
// Do we have the ebook cached? // Do we have the ebook cached?
try{ try{
/** @var Ebook $ebook */
$ebook = apcu_fetch('ebook-' . $wwwFilesystemPath); $ebook = apcu_fetch('ebook-' . $wwwFilesystemPath);
} }
catch(Safe\Exceptions\ApcuException){ catch(Safe\Exceptions\ApcuException){
@ -211,7 +213,7 @@ catch(Exceptions\EbookNotFoundException){
<p class="us-pd-warning">This ebook is thought to be free of copyright restrictions in the United States. It may still be under copyright in other countries. If youre not located in the United States, you must check your local laws to verify that this ebook is free of copyright restrictions in the country youre located in before accessing, downloading, or using it.</p> <p class="us-pd-warning">This ebook is thought to be free of copyright restrictions in the United States. It may still be under copyright in other countries. If youre not located in the United States, you must check your local laws to verify that this ebook is free of copyright restrictions in the country youre located in before accessing, downloading, or using it.</p>
<div class="downloads-container"> <div class="downloads-container">
<figure class="<? if($ebook->WordCount < 100000){ ?>small<? }elseif($ebook->WordCount >= 100000 && $ebook->WordCount < 200000){ ?>medium<? }elseif($ebook->WordCount >= 200000 && $ebook->WordCount <= 300000){ ?>large<? }elseif($ebook->WordCount >= 300000 && $ebook->WordCount < 400000){ ?>xlarge<? }elseif($ebook->WordCount >= 400000){ ?>xxlarge<? } ?>"> <figure class="<? if($ebook->WordCount < 100000){ ?>small<? }elseif($ebook->WordCount < 200000){ ?>medium<? }elseif($ebook->WordCount <= 300000){ ?>large<? }elseif($ebook->WordCount < 400000){ ?>xlarge<? }else{ ?>xxlarge<? } ?>">
<picture> <picture>
<source srcset="<?= $ebook->CoverImage2xAvifUrl ?> 2x, <?= $ebook->CoverImageAvifUrl ?> 1x" type="image/avif"/> <source srcset="<?= $ebook->CoverImage2xAvifUrl ?> 2x, <?= $ebook->CoverImageAvifUrl ?> 1x" type="image/avif"/>
<source srcset="<?= $ebook->CoverImage2xUrl ?> 2x, <?= $ebook->CoverImageUrl ?> 1x" type="image/jpg"/> <source srcset="<?= $ebook->CoverImage2xUrl ?> 2x, <?= $ebook->CoverImageUrl ?> 1x" type="image/jpg"/>

View file

@ -23,9 +23,11 @@ if($type === 'atom'){
} }
try{ try{
/** @var array<stdClass> $feeds */
$feeds = apcu_fetch('feeds-index-' . $type . '-' . $class); $feeds = apcu_fetch('feeds-index-' . $type . '-' . $class);
} }
catch(Safe\Exceptions\ApcuException){ catch(Safe\Exceptions\ApcuException){
/** @var array<stdClass> $feeds */
$feeds = Library::RebuildFeedsCache($type, $class); $feeds = Library::RebuildFeedsCache($type, $class);
} }
?><?= Template::Header(['title' => $ucType . ' Ebook Feeds by ' . $ucTitle, 'description' => 'A list of available ' . $ucType . ' feeds of Standard Ebooks ebooks by ' . $lcTitle . '.']) ?> ?><?= Template::Header(['title' => $ucType . ' Ebook Feeds by ' . $ucTitle, 'description' => 'A list of available ' . $ucType . ' feeds of Standard Ebooks ebooks by ' . $lcTitle . '.']) ?>

View file

@ -35,6 +35,7 @@ try{
throw new Exceptions\WebhookException('Couldn\'t understand HTTP request.', $post); throw new Exceptions\WebhookException('Couldn\'t understand HTTP request.', $post);
} }
/** @var array<array<string>> $data */
$data = json_decode($post, true); $data = json_decode($post, true);
// Decide what event we just received. // Decide what event we just received.

View file

@ -21,13 +21,16 @@ try{
throw new Exceptions\InvalidCredentialsException(); throw new Exceptions\InvalidCredentialsException();
} }
$post = json_decode(file_get_contents('php://input')); $post = file_get_contents('php://input');
if(!$post || !property_exists($post, 'RecordType')){ /** @var stdClass $data */
$data = json_decode($post);
if(!property_exists($data, 'RecordType')){
throw new Exceptions\WebhookException('Couldn\'t understand HTTP request.', $post); throw new Exceptions\WebhookException('Couldn\'t understand HTTP request.', $post);
} }
if($post->RecordType == 'SpamComplaint'){ if($data->RecordType == 'SpamComplaint'){
// Received when a user marks an email as spam // Received when a user marks an email as spam
$log->Write('Event type: spam complaint.'); $log->Write('Event type: spam complaint.');
@ -36,13 +39,13 @@ try{
from NewsletterSubscriptions ns from NewsletterSubscriptions ns
inner join Users u using(UserId) inner join Users u using(UserId)
where u.Email = ? where u.Email = ?
', [$post->Email]); ', [$data->Email]);
} }
elseif($post->RecordType == 'SubscriptionChange' && $post->SuppressSending){ elseif($data->RecordType == 'SubscriptionChange' && $data->SuppressSending){
// Received when a user clicks Postmark's "Unsubscribe" link in a newsletter email // Received when a user clicks Postmark's "Unsubscribe" link in a newsletter email
$log->Write('Event type: unsubscribe.'); $log->Write('Event type: unsubscribe.');
$email = $post->Recipient; $email = $data->Recipient;
// Remove the email from our newsletter list // Remove the email from our newsletter list
Db::Query(' Db::Query('
@ -54,18 +57,18 @@ try{
// Remove the suppression from Postmark, since we deleted it from our own list we will never email them again anyway // Remove the suppression from Postmark, since we deleted it from our own list we will never email them again anyway
$handle = curl_init(); $handle = curl_init();
curl_setopt($handle, CURLOPT_URL, 'https://api.postmarkapp.com/message-streams/' . $post->MessageStream . '/suppressions/delete'); curl_setopt($handle, CURLOPT_URL, 'https://api.postmarkapp.com/message-streams/' . $data->MessageStream . '/suppressions/delete');
curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1); curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($handle, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json', 'X-Postmark-Server-Token: ' . $smtpUsername]); curl_setopt($handle, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json', 'X-Postmark-Server-Token: ' . $smtpUsername]);
curl_setopt($handle, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($handle, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($handle, CURLOPT_POSTFIELDS, '{"Suppressions": [{"EmailAddress": "' . $email . '"}]}'); curl_setopt($handle, CURLOPT_POSTFIELDS, '{"Suppressions": [{"EmailAddress": "' . $email . '"}]}');
curl_exec($handle); curl_exec($handle);
} }
elseif($post->RecordType == 'SubscriptionChange' && $post->SuppressionReason === null){ elseif($data->RecordType == 'SubscriptionChange' && $data->SuppressionReason === null){
$log->Write('Event type: suppression deletion.'); $log->Write('Event type: suppression deletion.');
} }
else{ else{
$log->Write('Unrecognized event: ' . $post->RecordType); $log->Write('Unrecognized event: ' . $data->RecordType);
} }
$log->Write('Event processed.'); $log->Write('Event processed.');

View file

@ -23,6 +23,7 @@ try{
throw new Exceptions\InvalidCredentialsException(); throw new Exceptions\InvalidCredentialsException();
} }
/** @var stdClass $data */
$data = json_decode($post); $data = json_decode($post);
if($data->fromAddress == 'support@fracturedatlas.org' && strpos($data->subject, 'NOTICE:') !== false){ if($data->fromAddress == 'support@fracturedatlas.org' && strpos($data->subject, 'NOTICE:') !== false){