<?php

interface Visitable
{
    public function accept(Visitor $visitor);
}

interface Visitor
{
    public function visitUniversity(University $university): array;

    public function visitStudent(Student $student): string;
}

class University implements Visitable
{
    private $name;
    private $students;

    public function __construct(string $name, array $students)
    {
        $this->name = $name;
        $this->students = $students;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function getStudents(): array
    {
        return $this->students;
    }

    public function accept(Visitor $visitor): array
    {
        return $visitor->visitUniversity($this);
    }
}


class Student implements Visitable
{
    private $name;
    private $sickLeaves = [];

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function addSickLeave(\DateTime $start, \DateTime $end) : void
    {
        $this->sickLeaves[] = new SickLeave($start, $end);
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function getSickLeaves(): array
    {
        return $this->sickLeaves;
    }

    public function accept(Visitor $visitor): string
    {
        return $visitor->visitStudent($this);
    }
}


class SickLeaveReport implements Visitor
{
    public function visitUniversity(University $university): array
    {
        $msgs = [];
        $msgs[] = "Generating sick leave report for: \"{$university->getName()}\"";

        foreach ($university->getStudents() as $student) {
            $msgs[] = $this->visitStudent($student);
        }

        return $msgs;
    }

    public function visitStudent(Student $student): string
    {
        $daysMissed = 0;

        foreach ($student->getSickLeaves() as $sickLeave) {
            $daysMissed += $sickLeave->getStart()->diff($sickLeave->getEnd())->days + 1;
        }

        return "Student: {$student->getName()} missed {$daysMissed} days";
    }
}


class SickLeave
{
    private $start;
    private $end;

    public function __construct(\DateTime $start, \DateTime $end)
    {
        $this->start = $start;
        $this->end = $end;
    }

    public function getStart(): \DateTime
    {
        return $this->start;
    }

    public function getEnd(): \DateTime
    {
        return $this->end;
    }
}


// USAGE
$john = new Student("John");
$john->addSickLeave(new \DateTime("2019-10-01"), new \DateTime("2019-10-21"));
$john->addSickLeave(new \DateTime("2019-11-02"), new \DateTime("2019-11-10"));

$jan = new Student("Jan");
$jan->addSickLeave(new \DateTime("2019-11-01"), new \DateTime("2019-11-15"));

$ann = new Student("Ann");

$university = new University("Visitor University", [$john, $jan, $ann]);
$results = $university->accept(new SickLeaveReport());

function show($data)
{
    foreach ($data as $datum) {
        echo $datum.PHP_EOL;
    }
}

show($results);