fork download
  1. <?php
  2.  
  3. assert_options(ASSERT_ACTIVE, true);
  4. assert_options(ASSERT_WARNING, true);
  5.  
  6. class Alarm
  7. {
  8. const MONDAY = 1;
  9. const TUESDAY = 2;
  10. const WEDNESDAY = 3;
  11. const THURSDAY = 4;
  12. const FRIDAY = 5;
  13. const SATURDAY = 6;
  14. const SUNDAY = 7;
  15.  
  16. private const ALL_DAYS = [
  17. self::MONDAY,
  18. self::TUESDAY,
  19. self::WEDNESDAY,
  20. self::THURSDAY,
  21. self::FRIDAY,
  22. self::SATURDAY,
  23. self::SUNDAY
  24. ];
  25.  
  26. const TYPE_REPEATING = true;
  27. const TYPE_NON_REPEATING = false;
  28.  
  29. const STATUS_ACTIVE = true;
  30. const STATUS_INACTIVE = false;
  31.  
  32. private $time;
  33. private $daysOfWeek;
  34. private $type;
  35. private $status;
  36.  
  37. public function __construct(int $hour, int $minute)
  38. {
  39. $this->setTime($hour, $minute);
  40. $this->setDaysOfWeek(self::ALL_DAYS);
  41. $this->setType(self::TYPE_REPEATING);
  42. $this->setStatus(self::STATUS_ACTIVE);
  43. }
  44.  
  45. public function setTime(int $hour, int $minute): void
  46. {
  47. if ($hour > 23 || $hour < 0 || $minute > 60 || $minute < 0) {
  48. throw new InvalidArgumentException('Неправильное время');
  49. }
  50.  
  51. if ($hour < 10) {
  52. $hour = 0..$hour;
  53. }
  54.  
  55. if ($minute < 10) {
  56. $minute = 0..$minute;
  57. }
  58.  
  59. $this->time = $hour.':'.$minute;
  60. }
  61.  
  62. public function setDaysOfWeek(array $daysOfWeek): void
  63. {
  64. foreach ($daysOfWeek as $dayOfWeek) {
  65. if ($dayOfWeek > 7 || $dayOfWeek < 1) {
  66. throw new InvalidArgumentException('Неправильное значение в массиве с днями недели');
  67. }
  68. }
  69.  
  70. $this->daysOfWeek = $daysOfWeek;
  71. }
  72.  
  73. public function setType(bool $type): void
  74. {
  75. if ($this->status == Alarm::STATUS_INACTIVE && $type == Alarm::TYPE_NON_REPEATING) {
  76. throw new InvalidArgumentException('Нельзя делать одноразовой неактивную тревогу');
  77. }
  78.  
  79. $this->type = $type;
  80. }
  81.  
  82. public function setStatus(bool $status): void
  83. {
  84. if ($status == self::STATUS_INACTIVE && $this->type == self::TYPE_NON_REPEATING) {
  85. throw new InvalidArgumentException('Нельзя изменять статус одноразовой тревоги');
  86. }
  87.  
  88. $this->status = $status;
  89. }
  90.  
  91. public function getTime(): string
  92. {
  93. return $this->time;
  94. }
  95.  
  96. public function getDaysOfWeek(): array
  97. {
  98. return $this->daysOfWeek;
  99. }
  100.  
  101. public function isRepeating(): bool
  102. {
  103. return $this->type;
  104. }
  105.  
  106. public function isActive(): bool
  107. {
  108. return $this->status;
  109. }
  110.  
  111. public function findNearestAlarmTime(DateTimeImmutable $currentTime): DateTimeImmutable
  112. {
  113. $spelling = [
  114. '', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'
  115. ];
  116.  
  117. foreach ($this->daysOfWeek as $dayOfWeek) {
  118.  
  119. if ($dayOfWeek > 7 || $dayOfWeek < 1) {
  120. throw new InvalidArgumentException('Неправильное значение в массиве с днями недели');
  121. }
  122.  
  123. $ordinal = ($currentTime->format('H:i') > $this->time) ? 'next' : '';
  124. $datesAndTimes[] = $currentTime->modify($ordinal.' '.$spelling[$dayOfWeek].' '.$this->time);
  125.  
  126. }
  127.  
  128. sort($datesAndTimes);
  129.  
  130. return $datesAndTimes[0];
  131. }
  132. }
  133.  
  134. class AlarmClock
  135. {
  136. private $alarms;
  137.  
  138. public function addAlarm(Alarm $alarm): void
  139. {
  140. $this->alarms[] = $alarm;
  141. }
  142.  
  143. public function setAlarm(int $id, Alarm $alarm): void
  144. {
  145. if (!$this->alarms) {
  146. throw new Exception('Нет тревог');
  147. }
  148. if (!$this->alarms[$id]) {
  149. throw new InvalidArgumentException('Несуществующая тревога');
  150. }
  151.  
  152. $this->alarms[$id] = $alarm;
  153. }
  154.  
  155. public function getAlarm(int $id): ?Alarm
  156. {
  157. return $this->alarms[$id];
  158. }
  159.  
  160. public function deleteAlarm(Alarm $alarm): void
  161. {
  162. if (!$this->alarms) {
  163. throw new Exception('Нет тревог');
  164. }
  165.  
  166. foreach ($this->alarms as $key => $object) {
  167. if ($object == $alarm) {
  168. unset($this->alarms[$key]);
  169. }
  170. }
  171. }
  172.  
  173. public function findNearestAlarm(DateTimeImmutable $currentTime): Alarm
  174. {
  175. if (!$this->alarms) {
  176. throw new Exception('Нет тревог');
  177. }
  178.  
  179. $alarms = [];
  180.  
  181. foreach ($this->alarms as $alarm) {
  182.  
  183. $alarmTime = $alarm->findNearestAlarmTime($currentTime);
  184. $alarms[] = [$alarmTime, $alarm];
  185.  
  186. }
  187.  
  188. sort($alarms);
  189.  
  190. return $alarms[0][1];
  191. }
  192.  
  193. public function findTriggeredAlarm(DateTimeImmutable $currentTime): ?Alarm
  194. {
  195. if (!$this->alarms) {
  196. throw new Exception('Нет тревог');
  197. }
  198.  
  199. foreach ($this->alarms as $alarm) {
  200. if ($this->isTriggeredAlarm($alarm, $currentTime)
  201. && $alarm->isActive() == Alarm::STATUS_ACTIVE) {
  202. return $alarm;
  203. }
  204. }
  205.  
  206. return null;
  207. }
  208.  
  209. public function deleteProcessedNonRepeatingAlarms(DateTimeImmutable $currentTime): void
  210. {
  211. if (!$this->alarms) {
  212. throw new Exception('Нет тревог');
  213. }
  214.  
  215. foreach ($this->alarms as $alarm) {
  216. if ($this->isTriggeredAlarm($alarm, $currentTime)
  217. && $alarm->isRepeating() == Alarm::TYPE_NON_REPEATING) {
  218. $this->deleteAlarm($alarm);
  219. }
  220. }
  221. }
  222.  
  223. private function isTriggeredAlarm(Alarm $alarm, DateTimeImmutable $currentTime): bool
  224. {
  225. $alarmTime = $alarm->findNearestAlarmTime($currentTime);
  226. return ($alarmTime->format('Y-m-d H:i') == $currentTime->format('Y-m-d H:i'));
  227. }
  228. }
  229.  
  230. /*
  231.  * Тревога срабатывает только в установленное время
  232.  */
  233. $alarm = new Alarm(13, 0);
  234.  
  235. $alarmClock = new AlarmClock();
  236. $alarmClock->addAlarm($alarm);
  237.  
  238. $currentTime = new DateTimeImmutable('13:00');
  239. $ringingAlarm = $alarmClock->findTriggeredAlarm($currentTime);
  240.  
  241. assert($ringingAlarm !== null);
  242. assert($alarm === $ringingAlarm);
  243.  
  244. $currentTime2 = new DateTimeImmutable('14:00');
  245. $ringingAlarm2 = $alarmClock->findTriggeredAlarm($currentTime2);
  246.  
  247. assert($ringingAlarm2 === null);
  248.  
  249. echo "Тест пройден: Тревога срабатывает только в установленное время\n";
  250.  
  251. /*
  252.  * Многократная тревога сработает неограниченное число раз
  253.  */
  254. $alarm = new Alarm(13, 0);
  255.  
  256. $alarmClock = new AlarmClock();
  257. $alarmClock->addAlarm($alarm);
  258.  
  259. $currentTime = new DateTimeImmutable('13:00');
  260. $ringingAlarm = $alarmClock->findTriggeredAlarm($currentTime);
  261. assert($ringingAlarm === $alarm);
  262.  
  263. $currentTime2 = new DateTimeImmutable('+1 day 13:00');
  264. $ringingAlarm2 = $alarmClock->findTriggeredAlarm($currentTime2);
  265. assert($ringingAlarm2 === $alarm);
  266.  
  267. echo "Тест пройден: Многократная тревога срабатывает неограниченное число раз\n";
  268.  
  269. /*
  270.  * Если в Будильник добавить одноразовую Тревогу на будущее, то она сработает однократно
  271.  */
  272. $alarm = new Alarm(13, 0);
  273. $alarm->setType(Alarm::TYPE_NON_REPEATING);
  274. $alarm->setDaysOfWeek([Alarm::SUNDAY]);
  275.  
  276. $alarm2 = new Alarm(13, 30);
  277.  
  278. $alarmClock = new AlarmClock();
  279. $alarmClock->addAlarm($alarm);
  280. $alarmClock->addAlarm($alarm2);
  281.  
  282. $currentTime = new DateTimeImmutable('Sunday 13:00');
  283. $ringingAlarm = $alarmClock->findTriggeredAlarm($currentTime);
  284. assert($ringingAlarm === $alarm);
  285.  
  286. $alarmClock->deleteProcessedNonRepeatingAlarms($currentTime);
  287.  
  288. $currentTime2 = $currentTime->modify('next Sunday 13:00');
  289. $ringingAlarm2 = $alarmClock->findTriggeredAlarm($currentTime2);
  290. assert($ringingAlarm2 === null);
  291.  
  292. echo "Тест пройден: Одноразовая тревога добавленная на будущее срабатывает однократно\n";
  293.  
  294. /* Тревога срабатывает только в установленные дни */
  295. $alarm = new Alarm(13, 0);
  296. $alarm->setDaysOfWeek([Alarm::SATURDAY, Alarm::SUNDAY]);
  297.  
  298. $alarmClock = new AlarmClock();
  299. $alarmClock->addAlarm($alarm);
  300.  
  301. $currentTime = new DateTimeImmutable('Saturday 13:00');
  302. $ringingAlarm = $alarmClock->findTriggeredAlarm($currentTime);
  303. assert($ringingAlarm === $alarm);
  304.  
  305. $currentTime2 = new DateTimeImmutable('Sunday 13:00');
  306. $ringingAlarm2 = $alarmClock->findTriggeredAlarm($currentTime2);
  307. assert($ringingAlarm2 === $alarm);
  308.  
  309. $currentTime3 = new DateTimeImmutable('Monday 13:00');
  310. $ringingAlarm3 = $alarmClock->findTriggeredAlarm($currentTime3);
  311. assert($ringingAlarm3 === null);
  312.  
  313. echo "Тест пройден: Тревога срабатывает в установленные дни\n";
  314.  
  315. /* Неактивная тревога не срабатывает */
  316. $alarm = new Alarm(13, 0);
  317. $alarm->setStatus(Alarm::STATUS_INACTIVE);
  318.  
  319. $alarmClock = new AlarmClock();
  320. $alarmClock->addAlarm($alarm);
  321.  
  322. $currentTime = new DateTimeImmutable('13:00');
  323. $ringingAlarm = $alarmClock->findTriggeredAlarm($currentTime);
  324. assert($ringingAlarm === null);
  325.  
  326. echo "Тест пройден: Неактивная тревога не срабатывает\n";
  327.  
  328. /* Можно определить ближайшее время срабатывания даже для неактивной тревоги */
  329. $alarm = new Alarm(13, 0);
  330. $alarm2 = new Alarm(13, 30);
  331. $alarm2->setStatus(Alarm::STATUS_INACTIVE);
  332.  
  333. $currentTime = new DateTimeImmutable();
  334. $alarmTime = $alarm->findNearestAlarmTime($currentTime);
  335. assert(get_class($alarmTime) == DateTimeImmutable::class);
  336.  
  337. $currentTime2 = new DateTimeImmutable();
  338. $alarmTime2 = $alarm2->findNearestAlarmTime($currentTime2);
  339. assert(get_class($alarmTime2) == DateTimeImmutable::class);
  340.  
  341. echo "Тест пройден: Можно определить время срабатывания, в том числе для неактивной тревоги\n";
  342.  
  343. /* Можно удалить сработавшие одноразовые тревоги */
  344. $alarm = new Alarm(13, 0);
  345. $alarm->setType(Alarm::TYPE_NON_REPEATING);
  346. $alarm->setDaysOfWeek([Alarm::SUNDAY]);
  347.  
  348. $alarmClock = new AlarmClock();
  349. $alarmClock->addAlarm($alarm);
  350.  
  351. $currentTime = new DateTimeImmutable('Sunday 13:00');
  352. $alarmClock->deleteProcessedNonRepeatingAlarms($currentTime);
  353. assert($alarmClock->getAlarm(0) === null);
  354.  
  355. echo "Тест пройден: Можно удалить сработавшие одноразовые тревоги\n";
  356.  
  357. /* Можно узнать ближайшую сработающую Тревогу, даже если их несколько */
  358. $alarm = new Alarm(15, 50);
  359. $alarm2 = new Alarm(19, 0);
  360.  
  361. $alarmClock = new AlarmClock();
  362. $alarmClock->addAlarm($alarm);
  363. $alarmClock->addAlarm($alarm2);
  364.  
  365. $currentTime = new DateTimeImmutable();
  366. $nearestAlarm = $alarmClock->findNearestAlarm($currentTime);
  367. assert($nearestAlarm === $alarm || $nearestAlarm === $alarm2);
  368.  
  369. echo "Тест пройден: Можно узнать ближайщую тревогу, даже если их несколько\n";
  370.  
  371. /* Можно добавить Тревогу в Будильник */
  372. $alarm = new Alarm(13, 0);
  373.  
  374. $alarmClock = new AlarmClock();
  375.  
  376. $alarmClock->addAlarm($alarm);
  377. assert($alarmClock->getAlarm(0) === $alarm);
  378.  
  379. echo "Тест пройден: Можно добавить тревогу в будильник\n";
  380.  
  381. /* Можно удалить Тревогу из Будильника */
  382. $alarm = new Alarm(13, 0);
  383.  
  384. $alarmClock = new AlarmClock();
  385. $alarmClock->addAlarm($alarm);
  386.  
  387. $alarmClock->deleteAlarm($alarm);
  388. assert($alarmClock->getAlarm(0) === null);
  389.  
  390. echo "Тест пройден: Можно удалить тревогу из будильника\n";
  391.  
  392. /* Можно менять настройки Тревоги после добавления */
  393.  
  394. $alarm = new Alarm(13, 0);
  395.  
  396. $alarmClock = new AlarmClock();
  397. $alarmClock->addAlarm($alarm);
  398.  
  399. $alarm2 = new Alarm(14, 0);
  400. $alarm2->setType(Alarm::TYPE_NON_REPEATING);
  401.  
  402. $alarmClock->setAlarm(0, $alarm2);
  403.  
  404. assert($alarmClock->getAlarm(0) === $alarm2);
  405.  
  406. echo "Тест пройден: Можно менять настройки тревоги после добавления\n";
Success #stdin #stdout 0s 82624KB
stdin
Standard input is empty
stdout
Тест пройден: Тревога срабатывает только в установленное время
Тест пройден: Многократная тревога срабатывает неограниченное число раз
Тест пройден: Одноразовая тревога добавленная на будущее срабатывает однократно
Тест пройден: Тревога срабатывает в установленные дни
Тест пройден: Неактивная тревога не срабатывает
Тест пройден: Можно определить время срабатывания, в том числе для неактивной тревоги
Тест пройден: Можно удалить сработавшие одноразовые тревоги
Тест пройден: Можно узнать ближайщую тревогу, даже если их несколько
Тест пройден: Можно добавить тревогу в будильник
Тест пройден: Можно удалить тревогу из будильника
Тест пройден: Можно менять настройки тревоги после добавления