<?php
class Alarm
{
const MONDAY = 1;
const TUESDAY = 2;
const WEDNESDAY = 3;
const THURSDAY = 4;
const FRIDAY = 5;
const SATURDAY = 6;
const SUNDAY = 7;
private const ALL_DAYS = [
self::MONDAY,
self::TUESDAY,
self::WEDNESDAY,
self::THURSDAY,
self::FRIDAY,
self::SATURDAY,
self::SUNDAY
];
const TYPE_REPEATING = true;
const TYPE_NON_REPEATING = false;
const STATUS_ACTIVE = true;
const STATUS_INACTIVE = false;
private $time;
private $daysOfWeek;
private $type;
private $status;
public function __construct(int $hour, int $minute)
{
$this->setTime($hour, $minute);
$this->setDaysOfWeek(self::ALL_DAYS);
$this->setType(self::TYPE_REPEATING); $this->setStatus(self::STATUS_ACTIVE);
}
public function setTime(int $hour, int $minute): void
{
if ($hour > 23 || $hour < 0 || $minute > 60 || $minute < 0) {
throw new InvalidArgumentException('Неправильное время');
}
if ($hour < 10) {
$hour = 0..$hour;
}
if ($minute < 10) {
$minute = 0..$minute;
}
$this->time = $hour.':'.$minute; }
public function setDaysOfWeek
(array $daysOfWeek): void
{
foreach ($daysOfWeek as $dayOfWeek) {
if ($dayOfWeek > 7 || $dayOfWeek < 1) {
throw new InvalidArgumentException('Неправильное значение в массиве с днями недели');
}
}
$this->daysOfWeek = $daysOfWeek;
}
public function setType(bool
$type): void
{
if ($this->status == Alarm::STATUS_INACTIVE && $type == Alarm::TYPE_NON_REPEATING) {
throw new InvalidArgumentException('Нельзя делать одноразовой неактивную тревогу');
}
$this->type = $type;
}
public function setStatus(bool $status): void
{
if ($status == self::STATUS_INACTIVE && $this->type == self::TYPE_NON_REPEATING) {
throw new InvalidArgumentException('Нельзя изменять статус одноразовой тревоги');
}
$this->status = $status;
}
public function getTime(): string
{
}
public function getDaysOfWeek
(): array {
return $this->daysOfWeek;
}
public function isRepeating(): bool
{
return $this->type;
}
public function isActive(): bool
{
return $this->status;
}
public function findNearestAlarmTime(DateTimeImmutable $currentTime): DateTimeImmutable
{
$spelling = [
'', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'
];
foreach ($this->daysOfWeek as $dayOfWeek) {
if ($dayOfWeek > 7 || $dayOfWeek < 1) {
throw new InvalidArgumentException('Неправильное значение в массиве с днями недели');
}
$ordinal = ($currentTime->format('H:i') > $this->time) ?
'next' : ''; $datesAndTimes[] = $currentTime->modify($ordinal.' '.$spelling[$dayOfWeek].' '.$this->time);
}
return $datesAndTimes[0];
}
}
class AlarmClock
{
private $alarms;
public function addAlarm(Alarm $alarm): void
{
$this->alarms[] = $alarm;
}
public function setAlarm(int $id, Alarm $alarm): void
{
if (!$this->alarms) {
throw new Exception('Нет тревог');
}
if (!$this->alarms[$id]) {
throw new InvalidArgumentException('Несуществующая тревога');
}
$this->alarms[$id] = $alarm;
}
public function getAlarm(int $id): ?Alarm
{
return $this->alarms[$id];
}
public function deleteAlarm(Alarm $alarm): void
{
if (!$this->alarms) {
throw new Exception('Нет тревог');
}
foreach ($this->alarms as $key => $object) {
if ($object == $alarm) {
unset($this->alarms[$key]); }
}
}
public function findNearestAlarm(DateTimeImmutable $currentTime): Alarm
{
if (!$this->alarms) {
throw new Exception('Нет тревог');
}
$alarms = [];
foreach ($this->alarms as $alarm) {
$alarmTime = $alarm->findNearestAlarmTime($currentTime);
$alarms[] = [$alarmTime, $alarm];
}
return $alarms[0][1];
}
public function findTriggeredAlarm(DateTimeImmutable $currentTime): ?Alarm
{
if (!$this->alarms) {
throw new Exception('Нет тревог');
}
foreach ($this->alarms as $alarm) {
if ($this->isTriggeredAlarm($alarm, $currentTime)
&& $alarm->isActive() == Alarm::STATUS_ACTIVE) {
return $alarm;
}
}
return null;
}
public function deleteProcessedNonRepeatingAlarms(DateTimeImmutable $currentTime): void
{
if (!$this->alarms) {
throw new Exception('Нет тревог');
}
foreach ($this->alarms as $alarm) {
if ($this->isTriggeredAlarm($alarm, $currentTime)
&& $alarm->isRepeating() == Alarm::TYPE_NON_REPEATING) {
$this->deleteAlarm($alarm);
}
}
}
private function isTriggeredAlarm(Alarm $alarm, DateTimeImmutable $currentTime): bool
{
$alarmTime = $alarm->findNearestAlarmTime($currentTime);
return ($alarmTime->format('Y-m-d H:i') == $currentTime->format('Y-m-d H:i'));
}
}
/*
* Тревога срабатывает только в установленное время
*/
$alarm = new Alarm(13, 0);
$alarmClock = new AlarmClock();
$alarmClock->addAlarm($alarm);
$currentTime = new DateTimeImmutable('13:00');
$ringingAlarm = $alarmClock->findTriggeredAlarm($currentTime);
assert($ringingAlarm !== null); assert($alarm === $ringingAlarm);
$currentTime2 = new DateTimeImmutable('14:00');
$ringingAlarm2 = $alarmClock->findTriggeredAlarm($currentTime2);
assert($ringingAlarm2 === null);
echo "Тест пройден: Тревога срабатывает только в установленное время\n";
/*
* Многократная тревога сработает неограниченное число раз
*/
$alarm = new Alarm(13, 0);
$alarmClock = new AlarmClock();
$alarmClock->addAlarm($alarm);
$currentTime = new DateTimeImmutable('13:00');
$ringingAlarm = $alarmClock->findTriggeredAlarm($currentTime);
assert($ringingAlarm === $alarm);
$currentTime2 = new DateTimeImmutable('+1 day 13:00');
$ringingAlarm2 = $alarmClock->findTriggeredAlarm($currentTime2);
assert($ringingAlarm2 === $alarm);
echo "Тест пройден: Многократная тревога срабатывает неограниченное число раз\n";
/*
* Если в Будильник добавить одноразовую Тревогу на будущее, то она сработает однократно
*/
$alarm = new Alarm(13, 0);
$alarm->setType(Alarm
::TYPE_NON_REPEATING); $alarm->setDaysOfWeek([Alarm::SUNDAY]);
$alarm2 = new Alarm(13, 30);
$alarmClock = new AlarmClock();
$alarmClock->addAlarm($alarm);
$alarmClock->addAlarm($alarm2);
$currentTime = new DateTimeImmutable('Sunday 13:00');
$ringingAlarm = $alarmClock->findTriggeredAlarm($currentTime);
assert($ringingAlarm === $alarm);
$alarmClock->deleteProcessedNonRepeatingAlarms($currentTime);
$currentTime2 = $currentTime->modify('next Sunday 13:00');
$ringingAlarm2 = $alarmClock->findTriggeredAlarm($currentTime2);
assert($ringingAlarm2 === null);
echo "Тест пройден: Одноразовая тревога добавленная на будущее срабатывает однократно\n";
/* Тревога срабатывает только в установленные дни */
$alarm = new Alarm(13, 0);
$alarm->setDaysOfWeek([Alarm::SATURDAY, Alarm::SUNDAY]);
$alarmClock = new AlarmClock();
$alarmClock->addAlarm($alarm);
$currentTime = new DateTimeImmutable('Saturday 13:00');
$ringingAlarm = $alarmClock->findTriggeredAlarm($currentTime);
assert($ringingAlarm === $alarm);
$currentTime2 = new DateTimeImmutable('Sunday 13:00');
$ringingAlarm2 = $alarmClock->findTriggeredAlarm($currentTime2);
assert($ringingAlarm2 === $alarm);
$currentTime3 = new DateTimeImmutable('Monday 13:00');
$ringingAlarm3 = $alarmClock->findTriggeredAlarm($currentTime3);
assert($ringingAlarm3 === null);
echo "Тест пройден: Тревога срабатывает в установленные дни\n";
/* Неактивная тревога не срабатывает */
$alarm = new Alarm(13, 0);
$alarm->setStatus(Alarm::STATUS_INACTIVE);
$alarmClock = new AlarmClock();
$alarmClock->addAlarm($alarm);
$currentTime = new DateTimeImmutable('13:00');
$ringingAlarm = $alarmClock->findTriggeredAlarm($currentTime);
assert($ringingAlarm === null);
echo "Тест пройден: Неактивная тревога не срабатывает\n";
/* Можно определить ближайшее время срабатывания даже для неактивной тревоги */
$alarm = new Alarm(13, 0);
$alarm2 = new Alarm(13, 30);
$alarm2->setStatus(Alarm::STATUS_INACTIVE);
$currentTime = new DateTimeImmutable();
$alarmTime = $alarm->findNearestAlarmTime($currentTime);
$currentTime2 = new DateTimeImmutable();
$alarmTime2 = $alarm2->findNearestAlarmTime($currentTime2);
echo "Тест пройден: Можно определить время срабатывания, в том числе для неактивной тревоги\n";
/* Можно удалить сработавшие одноразовые тревоги */
$alarm = new Alarm(13, 0);
$alarm->setType(Alarm
::TYPE_NON_REPEATING); $alarm->setDaysOfWeek([Alarm::SUNDAY]);
$alarmClock = new AlarmClock();
$alarmClock->addAlarm($alarm);
$currentTime = new DateTimeImmutable('Sunday 13:00');
$alarmClock->deleteProcessedNonRepeatingAlarms($currentTime);
assert($alarmClock->getAlarm(0) === null);
echo "Тест пройден: Можно удалить сработавшие одноразовые тревоги\n";
/* Можно узнать ближайшую сработающую Тревогу, даже если их несколько */
$alarm = new Alarm(15, 50);
$alarm2 = new Alarm(19, 0);
$alarmClock = new AlarmClock();
$alarmClock->addAlarm($alarm);
$alarmClock->addAlarm($alarm2);
$currentTime = new DateTimeImmutable();
$nearestAlarm = $alarmClock->findNearestAlarm($currentTime);
assert($nearestAlarm === $alarm || $nearestAlarm === $alarm2);
echo "Тест пройден: Можно узнать ближайщую тревогу, даже если их несколько\n";
/* Можно добавить Тревогу в Будильник */
$alarm = new Alarm(13, 0);
$alarmClock = new AlarmClock();
$alarmClock->addAlarm($alarm);
assert($alarmClock->getAlarm(0) === $alarm);
echo "Тест пройден: Можно добавить тревогу в будильник\n";
/* Можно удалить Тревогу из Будильника */
$alarm = new Alarm(13, 0);
$alarmClock = new AlarmClock();
$alarmClock->addAlarm($alarm);
$alarmClock->deleteAlarm($alarm);
assert($alarmClock->getAlarm(0) === null);
echo "Тест пройден: Можно удалить тревогу из будильника\n";
/* Можно менять настройки Тревоги после добавления */
$alarm = new Alarm(13, 0);
$alarmClock = new AlarmClock();
$alarmClock->addAlarm($alarm);
$alarm2 = new Alarm(14, 0);
$alarm2->setType(Alarm
::TYPE_NON_REPEATING);
$alarmClock->setAlarm(0, $alarm2);
assert($alarmClock->getAlarm(0) === $alarm2);
echo "Тест пройден: Можно менять настройки тревоги после добавления\n";