<?php

class Employee {

    public $name;
    public $position;

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


use SplObjectStorage, IteratorAggregate, Countable;

class CompanyEmployeeTeam implements IteratorAggregate, Countable {

    public $person;
    private $subordinates;

    public function __construct (Employee $person) {
        $this->subordinates = new SplObjectStorage();
        $this->person = $person;
    }

    public function addSubordinate (CompanyEmployeeTeam $subordinate): void {
        $this->subordinates->attach($subordinate);
    }

    public function removeSubordinate (CompanyEmployeeTeam $subordinate): void {
        $this->subordinates->detach($subordinate);
    }

    public function getSubordinates (): SplObjectStorage {
        return $this->subordinates;
    }

    public function getIterator (): EmployeeTeamIterator {
        return new EmployeeTeamIterator($this);
    }

    public function count (): int {
        return count(new EmployeeTeamIterator($this));
    }

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

    public function getPosition (): string {
        return $this->person->position;
    }
}


use Iterator, Countable;

class EmployeeTeamIterator implements Iterator, Countable {

    private $position = 0;
    private $teamMembers = [];

    public function __construct (CompanyEmployeeTeam $employee) {
        $this->addTeam($employee);
        $this->position = 0;
    }

    protected function addTeam (CompanyEmployeeTeam $employee): void {
        foreach ($employee->getSubordinates() as $member) {
            array_push($this->teamMembers, $member);
            $this->addTeam($member);
        }
    }

    public function current (): CompanyEmployeeTeam {
        return $this->teamMembers[$this->position];
    }

    public function next (): void {
        ++$this->position;
    }

    public function key (): int {
        return $this->position;
    }

    public function valid (): bool {
        return isset($this->teamMembers[$this->position]);
    }

    public function rewind (): void {
        $this->position = 0;
    }

    public function count (): int {
        return count($this->teamMembers);
    }
}


// USAGE

$john = new CompanyEmployeeTeam(new Employee("John", 'Director'));
$tom = new CompanyEmployeeTeam(new Employee("Tom", 'Developer'));
$johny = new CompanyEmployeeTeam(new Employee("Johny", 'Developer'));
$ceo = new CompanyEmployeeTeam(new Employee("Mark", "CEO"));

$john->addSubordinate($tom);
$john->addSubordinate($johny);
$ceo->addSubordinate($john);

showTeam($ceo);

$jan = new CompanyEmployeeTeam(new Employee("Jan", 'Developer'));
$ceo->addSubordinate($jan);

showTeam($ceo);

$ceo->removeSubordinate($jan);

showTeam($ceo);

function showTeam ($users) {
    foreach ($users as $user) {
        echo $user->getPosition() . ": " . $user->getName() . PHP_EOL;
    }
    echo "Number of subordinates of " . $users->getName() . ": " . count($users) . PHP_EOL;
}