Загрузка данных


<?php
declare(strict_types=1);

class Reader {
    public function __construct(
        private int $id,
        private string $name,
        private string $email
    ) {}

    public function getId(): int { return $this->id; }
    public function getName(): string { return $this->name; }
    public function getEmail(): string { return $this->email; }
}

class Book {
    public function __construct(
        private string $isbn,
        private string $title,
        private string $author,
        private int $copies
    ) {}

    public function getTitle(): string { return $this->title; }
    public function isAvailable(): bool { return $this->copies > 0; }

    public function take(): void {
        if (!$this->isAvailable()) {
            throw new RuntimeException("Нет свободных экземпляров");
        }
        $this->copies--;
    }

    public function giveBack(): void { $this->copies++; }
}

class Booking {
    private bool $returned = false;

    public function __construct(
        private Reader $reader,
        private Book $book,
        private DateTimeImmutable $dueDate
    ) {}

    public function getReader(): Reader { return $this->reader; }
    public function getBook(): Book { return $this->book; }

    public function isOverdue(): bool {
        return !$this->returned && new DateTimeImmutable() > $this->dueDate;
    }

    public function close(): void {
        if (!$this->returned) {
            $this->returned = true;
            $this->book->giveBack();
        }
    }
}

class Library {
    private array $books = [];
    private array $readers = [];
    private array $bookings = [];

    public function addBook(Book $book): void { $this->books[] = $book; }

    public function registerReader(Reader $reader): void { $this->readers[] = $reader; }

    public function findByTitle(string $title): array {
        return array_values(array_filter(
            $this->books,
            fn(Book $b) => mb_stripos($b->getTitle(), $title) !== false
        ));
    }

    public function issueBook(Reader $reader, Book $book, int $days = 14): Booking {
        $book->take();
        $booking = new Booking($reader, $book, new DateTimeImmutable("+$days days"));
        $this->bookings[] = $booking;
        return $booking;
    }

    public function returnBook(Booking $booking): void { $booking->close(); }

    public function getOverdueNotifications(): array {
        $messages = [];
        foreach ($this->bookings as $booking) {
            if ($booking->isOverdue()) {
                $messages[] = sprintf(
                    "%s (%s), верните книгу «%s»",
                    $booking->getReader()->getName(),
                    $booking->getReader()->getEmail(),
                    $booking->getBook()->getTitle()
                );
            }
        }
        return $messages;
    }
}