Clean up the cleaning up
This commit is contained in:
parent
f67873467e
commit
19922907c8
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "fwdekker/death-notifier",
|
||||
"description": "Get notified when a famous person dies.",
|
||||
"version": "0.15.5", "_comment_version": "Also update version in `package.json`!",
|
||||
"version": "0.15.6", "_comment_version": "Also update version in `package.json`!",
|
||||
"type": "project",
|
||||
"license": "MIT",
|
||||
"homepage": "https://git.fwdekker.com/tools/death-notifier",
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "death-notifier",
|
||||
"version": "0.15.5", "_comment_version": "Also update version in `composer.json`!",
|
||||
"version": "0.15.6", "_comment_version": "Also update version in `composer.json`!",
|
||||
"description": "Get notified when a famous person dies.",
|
||||
"author": "Florine W. Dekker",
|
||||
"browser": "dist/bundle.js",
|
||||
|
|
|
@ -3,18 +3,19 @@
|
|||
use com\fwdekker\deathnotifier\ActionDispatcher;
|
||||
use com\fwdekker\deathnotifier\ActionMethod;
|
||||
use com\fwdekker\deathnotifier\Database;
|
||||
use com\fwdekker\deathnotifier\EmulateCronCliAction;
|
||||
use com\fwdekker\deathnotifier\EmulateCronAction;
|
||||
use com\fwdekker\deathnotifier\LoggerUtil;
|
||||
use com\fwdekker\deathnotifier\mailer\MailManager;
|
||||
use com\fwdekker\deathnotifier\mailer\ProcessEmailQueueCliAction;
|
||||
use com\fwdekker\deathnotifier\mailer\ProcessEmailQueueAction;
|
||||
use com\fwdekker\deathnotifier\mediawiki\MediaWiki;
|
||||
use com\fwdekker\deathnotifier\ProtectedAction;
|
||||
use com\fwdekker\deathnotifier\Response;
|
||||
use com\fwdekker\deathnotifier\StartSessionAction;
|
||||
use com\fwdekker\deathnotifier\tracking\AddTrackingAction;
|
||||
use com\fwdekker\deathnotifier\tracking\ListTrackingsAction;
|
||||
use com\fwdekker\deathnotifier\tracking\RemoveTrackingAction;
|
||||
use com\fwdekker\deathnotifier\tracking\TrackingManager;
|
||||
use com\fwdekker\deathnotifier\tracking\UpdateTrackingsCliAction;
|
||||
use com\fwdekker\deathnotifier\tracking\UpdateTrackingsAction;
|
||||
use com\fwdekker\deathnotifier\user\GetUserDataAction;
|
||||
use com\fwdekker\deathnotifier\user\LoginAction;
|
||||
use com\fwdekker\deathnotifier\user\LogoutAction;
|
||||
|
@ -41,6 +42,11 @@ $config = Util::read_config();
|
|||
LoggerUtil::configure($config["logger"]);
|
||||
$logger = LoggerUtil::with_name();
|
||||
|
||||
if (hash_equals($config["admin"]["cli_secret"], "REPLACE THIS WITH A SECRET VALUE")) {
|
||||
$logger->error("You must set a CLI secret in the configuration file before running Death Notifier.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$db = new Database($config["database"]["filename"]);
|
||||
|
||||
$mediawiki = new MediaWiki();
|
||||
|
@ -82,12 +88,12 @@ try {
|
|||
$dispatcher->register_action(new RemoveTrackingAction($tracking_manager));
|
||||
// CLI actions
|
||||
$cli_actions = [
|
||||
new UpdateTrackingsCliAction($config, $db->conn, $tracking_manager, $mediawiki, $mail_manager),
|
||||
new ProcessEmailQueueCliAction($config, $mail_manager),
|
||||
new UpdateTrackingsAction($db->conn, $tracking_manager, $mediawiki, $mail_manager, $config["admin"]["cli_secret"]),
|
||||
new ProcessEmailQueueAction($config, $mail_manager, $config["admin"]["cli_secret"]),
|
||||
];
|
||||
$dispatcher->register_action($cli_actions[0]);
|
||||
$dispatcher->register_action($cli_actions[1]);
|
||||
$dispatcher->register_action(new EmulateCronCliAction($cli_actions));
|
||||
$dispatcher->register_action(new EmulateCronAction($cli_actions));
|
||||
// Dispatch
|
||||
if (isset($_GET["action"]))
|
||||
$response = $dispatcher->handle(ActionMethod::GET);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# TODO: Add i18n
|
||||
|
||||
[admin]
|
||||
# TODO: Replace stored password with hash of password
|
||||
# Password to use the CLI of `api.php`. Until this value is changed from its default, the feature is disabled
|
||||
cli_secret = REPLACE THIS WITH A SECRET VALUE
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ use com\fwdekker\deathnotifier\validator\IsEqualToRule;
|
|||
use com\fwdekker\deathnotifier\validator\IsNotSetRule;
|
||||
use com\fwdekker\deathnotifier\validator\IsSetRule;
|
||||
use com\fwdekker\deathnotifier\validator\Rule;
|
||||
use com\fwdekker\deathnotifier\validator\ValidationException;
|
||||
use InvalidArgumentException;
|
||||
|
||||
|
||||
|
@ -66,6 +67,7 @@ abstract class Action
|
|||
$this->method = $method;
|
||||
$this->name = $name;
|
||||
|
||||
// TODO: Move authorisation-related validation to `dispatch` method?
|
||||
$this->require_logged_in = $require_logged_in;
|
||||
$this->require_logged_out = $require_logged_out;
|
||||
$this->require_valid_csrf_token = $require_valid_csrf_token;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier;
|
||||
|
||||
use com\fwdekker\deathnotifier\validator\ValidationException;
|
||||
use InvalidArgumentException;
|
||||
|
||||
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace com\fwdekker\deathnotifier;
|
||||
|
||||
use com\fwdekker\deathnotifier\validator\Rule;
|
||||
|
||||
|
||||
/**
|
||||
* An action for the CLI, which requires a valid password to be executed.
|
||||
*/
|
||||
abstract class CliAction extends Action
|
||||
{
|
||||
/**
|
||||
* @var array<string, mixed> the application's configuration
|
||||
*/
|
||||
protected mixed $config;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new CLI action.
|
||||
*
|
||||
* @param array<string, mixed> $config the application's configuration
|
||||
* @param string $name the name of the action that this action handles
|
||||
* @param array<string, Rule[]> $rule_lists maps input keys to {@see Rule}s that should be validated before this
|
||||
* action is handled
|
||||
*/
|
||||
public function __construct(mixed $config, string $name, array $rule_lists = [])
|
||||
{
|
||||
parent::__construct(ActionMethod::CLI, $name, rule_lists: $rule_lists);
|
||||
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates the admin password, and then validates remaining inputs as with any {@see Action}.
|
||||
*
|
||||
* @return void
|
||||
* @throws ValidationException if the input is invalid
|
||||
*/
|
||||
public function validate_inputs(): void
|
||||
{
|
||||
$inputs = $this->method->get_inputs();
|
||||
if (hash_equals($this->config["admin"]["cli_secret"], "REPLACE THIS WITH A SECRET VALUE"))
|
||||
throw new ValidationException("Default config value for 'cli_secret' detected. CLI access disabled.");
|
||||
if (!isset($inputs["password"]))
|
||||
throw new ValidationException("Password required. Specify a password using `password=...`.");
|
||||
// TODO: Read input password from file specified as an input argument in `$argv` (= `$inputs`)
|
||||
if (!hash_equals($this->config["admin"]["cli_secret"], $inputs["password"]))
|
||||
throw new ValidationException("Incorrect password.");
|
||||
|
||||
parent::validate_inputs();
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ class Database
|
|||
*/
|
||||
public function __construct(string $filename)
|
||||
{
|
||||
$this->logger = LoggerUtil::with_name("Database");
|
||||
$this->logger = LoggerUtil::with_name($this::class);
|
||||
|
||||
$this->conn = new PDO("sqlite:$filename", options: array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
|
||||
$this->conn->exec("PRAGMA foreign_keys = ON;");
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
namespace com\fwdekker\deathnotifier;
|
||||
|
||||
|
||||
use com\fwdekker\deathnotifier\validator\ValidationException;
|
||||
|
||||
/**
|
||||
* Periodically executes several other actions, as if cron jobs have been set up to do so.
|
||||
*
|
||||
* Intended for local development only.
|
||||
*/
|
||||
class EmulateCronCliAction extends Action
|
||||
class EmulateCronAction extends Action
|
||||
{
|
||||
/**
|
||||
* The number of seconds between executing tasks.
|
||||
|
@ -57,7 +59,7 @@ class EmulateCronCliAction extends Action
|
|||
print("Emulating cron jobs.\n");
|
||||
foreach ($this->actions as $action)
|
||||
$action->handle();
|
||||
print("Done\n");
|
||||
print("Done.\n");
|
||||
|
||||
sleep(self::INTERVAL);
|
||||
}
|
|
@ -24,7 +24,7 @@ class LoggerUtil
|
|||
/**
|
||||
* Configures the main logger.
|
||||
*
|
||||
* @param array $config the logger configuration
|
||||
* @param array<string, mixed> $config the logger configuration
|
||||
* @return void
|
||||
*/
|
||||
public static function configure(array $config): void
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier\mailer;
|
||||
|
||||
use com\fwdekker\deathnotifier\Action;
|
||||
use com\fwdekker\deathnotifier\ActionException;
|
||||
use com\fwdekker\deathnotifier\CliAction;
|
||||
use com\fwdekker\deathnotifier\ActionMethod;
|
||||
use com\fwdekker\deathnotifier\LoggerUtil;
|
||||
use com\fwdekker\deathnotifier\validator\IsEqualToRule;
|
||||
use Monolog\Logger;
|
||||
use PHPMailer\PHPMailer\Exception as PHPMailerException;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
|
@ -14,12 +16,16 @@ use PHPMailer\PHPMailer\SMTP;
|
|||
/**
|
||||
* Processes the queue of emails to send.
|
||||
*/
|
||||
class ProcessEmailQueueCliAction extends CliAction
|
||||
class ProcessEmailQueueAction extends Action
|
||||
{
|
||||
/**
|
||||
* @var Logger the logger to log with
|
||||
*/
|
||||
private readonly Logger $logger;
|
||||
/**
|
||||
* @var array<string, mixed> the application's configuration
|
||||
*/
|
||||
private readonly array $config;
|
||||
/**
|
||||
* @var MailManager the manager to process the queue with
|
||||
*/
|
||||
|
@ -29,14 +35,20 @@ class ProcessEmailQueueCliAction extends CliAction
|
|||
/**
|
||||
* Constructs a new `ProcessEmailQueueAction`.
|
||||
*
|
||||
* @param mixed $config the application's configuration
|
||||
* @param array<string, mixed> $config the application's configuration
|
||||
* @param MailManager $mail_manager the manager to process the queue with
|
||||
* @param string $password the admin password required to perform this function
|
||||
*/
|
||||
public function __construct(mixed $config, MailManager $mail_manager)
|
||||
public function __construct(array $config, MailManager $mail_manager, string $password)
|
||||
{
|
||||
parent::__construct($config, "process-email-queue");
|
||||
parent::__construct(
|
||||
ActionMethod::CLI,
|
||||
"process-email-queue",
|
||||
rule_lists: ["password" => [new IsEqualToRule($password, "Incorrect password.")]],
|
||||
);
|
||||
|
||||
$this->logger = LoggerUtil::with_name("ProcessEmailQueueCliAction");
|
||||
$this->logger = LoggerUtil::with_name($this::class);
|
||||
$this->config = $config;
|
||||
$this->mail_manager = $mail_manager;
|
||||
}
|
||||
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier\mediawiki;
|
||||
|
||||
use com\fwdekker\deathnotifier\ValidationException;
|
||||
use com\fwdekker\deathnotifier\LoggerUtil;
|
||||
use com\fwdekker\deathnotifier\validator\Rule;
|
||||
use com\fwdekker\deathnotifier\validator\ValidationException;
|
||||
use Monolog\Logger;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -11,6 +13,10 @@ use com\fwdekker\deathnotifier\validator\Rule;
|
|||
*/
|
||||
class IsPersonPageRule extends Rule
|
||||
{
|
||||
/**
|
||||
* @var Logger the logger to log with
|
||||
*/
|
||||
private Logger $logger;
|
||||
/**
|
||||
* @var MediaWiki the instance to connect to Wikipedia with
|
||||
*/
|
||||
|
@ -28,6 +34,7 @@ class IsPersonPageRule extends Rule
|
|||
{
|
||||
parent::__construct($override_message);
|
||||
|
||||
$this->logger = LoggerUtil::with_name($this::class);
|
||||
$this->mediawiki = $mediawiki;
|
||||
}
|
||||
|
||||
|
@ -47,16 +54,14 @@ class IsPersonPageRule extends Rule
|
|||
$person_name = $inputs[$key];
|
||||
try {
|
||||
$info = $this->mediawiki->query_person_info([$person_name]);
|
||||
} catch (MediaWikiException) {
|
||||
// TODO: Log this exception!
|
||||
// $this->logger->error("Failed to query page info.", ["cause" => $exception, "name" => $person_name]);
|
||||
} catch (MediaWikiException $exception) {
|
||||
$this->logger->error("Failed to query page info.", ["cause" => $exception, "name" => $person_name]);
|
||||
throw new ValidationException(
|
||||
$this->override_message ?? "Could not reach Wikipedia. Maybe the website is down?"
|
||||
);
|
||||
}
|
||||
|
||||
$normalized_name = $info->redirects[$person_name];
|
||||
$status = $info->results[$normalized_name]["status"];
|
||||
$type = $info->results[$normalized_name]["type"];
|
||||
|
||||
if (in_array($normalized_name, $info->missing))
|
||||
|
|
|
@ -44,7 +44,7 @@ class MediaWiki
|
|||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logger = LoggerUtil::with_name("MediaWiki");
|
||||
$this->logger = LoggerUtil::with_name($this::class);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,12 +6,14 @@ use com\fwdekker\deathnotifier\Action;
|
|||
use com\fwdekker\deathnotifier\ActionException;
|
||||
use com\fwdekker\deathnotifier\ActionMethod;
|
||||
use com\fwdekker\deathnotifier\IllegalStateException;
|
||||
use com\fwdekker\deathnotifier\LoggerUtil;
|
||||
use com\fwdekker\deathnotifier\mediawiki\IsPersonPageRule;
|
||||
use com\fwdekker\deathnotifier\mediawiki\MediaWiki;
|
||||
use com\fwdekker\deathnotifier\mediawiki\MediaWikiException;
|
||||
use com\fwdekker\deathnotifier\ValidationException;
|
||||
use com\fwdekker\deathnotifier\validator\HasLengthRule;
|
||||
use com\fwdekker\deathnotifier\validator\IsNotBlankRule;
|
||||
use com\fwdekker\deathnotifier\validator\ValidationException;
|
||||
use Monolog\Logger;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -19,6 +21,10 @@ use com\fwdekker\deathnotifier\validator\IsNotBlankRule;
|
|||
*/
|
||||
class AddTrackingAction extends Action
|
||||
{
|
||||
/**
|
||||
* @var Logger the logger to log with
|
||||
*/
|
||||
private Logger $logger;
|
||||
/**
|
||||
* @var TrackingManager the manager to add the tracking to
|
||||
*/
|
||||
|
@ -51,6 +57,7 @@ class AddTrackingAction extends Action
|
|||
],
|
||||
);
|
||||
|
||||
$this->logger = LoggerUtil::with_name($this::class);
|
||||
$this->tracking_manager = $tracking_manager;
|
||||
$this->mediawiki = $mediawiki;
|
||||
}
|
||||
|
@ -72,8 +79,7 @@ class AddTrackingAction extends Action
|
|||
try {
|
||||
$info = $this->mediawiki->query_person_info([$person_name]);
|
||||
} catch (MediaWikiException $exception) {
|
||||
// TODO: Log this?
|
||||
// $this->logger->error("Failed to query page info.", ["cause" => $exception, "name" => $person_name]);
|
||||
$this->logger->error("Failed to query page info.", ["cause" => $exception, "name" => $person_name]);
|
||||
throw new ActionException("Could not reach Wikipedia. Maybe the website is down?");
|
||||
}
|
||||
|
||||
|
|
|
@ -24,10 +24,6 @@ class TrackingManager
|
|||
*/
|
||||
public const MAX_TITLE_LENGTH = 255;
|
||||
|
||||
/**
|
||||
* @var Logger the logger to log with
|
||||
*/
|
||||
private Logger $logger;
|
||||
/**
|
||||
* @var PDO the database connection to interact with
|
||||
*/
|
||||
|
@ -41,7 +37,6 @@ class TrackingManager
|
|||
*/
|
||||
public function __construct(PDO $conn)
|
||||
{
|
||||
$this->logger = LoggerUtil::with_name("TrackingManager");
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,20 +2,28 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier\tracking;
|
||||
|
||||
use com\fwdekker\deathnotifier\Action;
|
||||
use com\fwdekker\deathnotifier\ActionException;
|
||||
use com\fwdekker\deathnotifier\CliAction;
|
||||
use com\fwdekker\deathnotifier\ActionMethod;
|
||||
use com\fwdekker\deathnotifier\Database;
|
||||
use com\fwdekker\deathnotifier\LoggerUtil;
|
||||
use com\fwdekker\deathnotifier\mailer\MailManager;
|
||||
use com\fwdekker\deathnotifier\mediawiki\MediaWiki;
|
||||
use com\fwdekker\deathnotifier\mediawiki\MediaWikiException;
|
||||
use com\fwdekker\deathnotifier\validator\IsEqualToRule;
|
||||
use Monolog\Logger;
|
||||
use PDO;
|
||||
|
||||
|
||||
/**
|
||||
* Updates all trackings that users have added.
|
||||
*/
|
||||
class UpdateTrackingsCliAction extends CliAction
|
||||
class UpdateTrackingsAction extends Action
|
||||
{
|
||||
/**
|
||||
* @var Logger the logger to log with
|
||||
*/
|
||||
private Logger $logger;
|
||||
/**
|
||||
* @var PDO the database connection to interact with
|
||||
*/
|
||||
|
@ -37,17 +45,22 @@ class UpdateTrackingsCliAction extends CliAction
|
|||
/**
|
||||
* Constructs a new `UpdateTrackingsAction`.
|
||||
*
|
||||
* @param mixed $config the application's configuration
|
||||
* @param PDO $conn the database connection to interact with
|
||||
* @param TrackingManager $tracking_manager the manager through which trackings should be updated
|
||||
* @param MediaWiki $mediawiki the instance to connect to Wikipedia with
|
||||
* @param MailManager $mailer the mailer to send emails with
|
||||
* @param string $password the admin password required to perform this function
|
||||
*/
|
||||
public function __construct(mixed $config, PDO $conn, TrackingManager $tracking_manager, MediaWiki $mediawiki,
|
||||
MailManager $mailer)
|
||||
public function __construct(PDO $conn, TrackingManager $tracking_manager, MediaWiki $mediawiki, MailManager $mailer,
|
||||
string $password)
|
||||
{
|
||||
parent::__construct($config, "update-trackings");
|
||||
parent::__construct(
|
||||
ActionMethod::CLI,
|
||||
"update-trackings",
|
||||
rule_lists: ["password" => [new IsEqualToRule($password, "Incorrect password.")]],
|
||||
);
|
||||
|
||||
$this->logger = LoggerUtil::with_name($this::class);
|
||||
$this->conn = $conn;
|
||||
$this->tracking_manager = $tracking_manager;
|
||||
$this->mediawiki = $mediawiki;
|
||||
|
@ -64,15 +77,14 @@ class UpdateTrackingsCliAction extends CliAction
|
|||
public function handle(): mixed
|
||||
{
|
||||
$names = $this->tracking_manager->list_all_unique_person_names();
|
||||
if (empty($names)) return;
|
||||
if (empty($names)) return null;
|
||||
|
||||
// Fetch changes
|
||||
try {
|
||||
$people_statuses = $this->mediawiki->query_person_info($names);
|
||||
} catch (MediaWikiException) {
|
||||
} catch (MediaWikiException $exception) {
|
||||
$this->logger->error("Failed to query page info.", ["cause" => $exception, "pages" => $names]);
|
||||
throw new ActionException("Could not reach Wikipedia. Maybe the website is down?");
|
||||
// TODO: Log this exception
|
||||
// $this->logger->error("Failed to retrieve page information.", ["cause" => $exception, "pages" => $names]);
|
||||
}
|
||||
|
||||
// Process changes
|
|
@ -63,7 +63,7 @@ class UserManager
|
|||
*/
|
||||
public function __construct(PDO $conn, MailManager $mailer)
|
||||
{
|
||||
$this->logger = LoggerUtil::with_name("UserManager");
|
||||
$this->logger = LoggerUtil::with_name($this::class);
|
||||
$this->conn = $conn;
|
||||
$this->mailer = $mailer;
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier\validator;
|
||||
|
||||
use com\fwdekker\deathnotifier\ValidationException;
|
||||
|
||||
|
||||
/**
|
||||
* Verifies that the input is of the specific length.
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier\validator;
|
||||
|
||||
use com\fwdekker\deathnotifier\ValidationException;
|
||||
|
||||
|
||||
/**
|
||||
* Verifies that the input is an email address.
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier\validator;
|
||||
|
||||
use com\fwdekker\deathnotifier\ValidationException;
|
||||
|
||||
|
||||
/**
|
||||
* Verifies that the input has the specified value.
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier\validator;
|
||||
|
||||
use com\fwdekker\deathnotifier\ValidationException;
|
||||
|
||||
|
||||
/**
|
||||
* Verifies that the input is not blank.
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier\validator;
|
||||
|
||||
use com\fwdekker\deathnotifier\ValidationException;
|
||||
|
||||
|
||||
/**
|
||||
* Verifies that the input is not set.
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier\validator;
|
||||
|
||||
use com\fwdekker\deathnotifier\ValidationException;
|
||||
|
||||
|
||||
/**
|
||||
* Verifies that the input is set.
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier\validator;
|
||||
|
||||
use com\fwdekker\deathnotifier\Response;
|
||||
use com\fwdekker\deathnotifier\ValidationException;
|
||||
|
||||
|
||||
/**
|
||||
* A rule/constraint/assertion that should hold over an input.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace com\fwdekker\deathnotifier;
|
||||
namespace com\fwdekker\deathnotifier\validator;
|
||||
|
||||
use Exception;
|
||||
|
||||
|
|
Loading…
Reference in New Issue