public static function traverse(Digraph $graph, DepthFirstVisitorInterface $visitor, $start = NULL) {
        if ($start === NULL) {
            $queue = self::find_sources($graph, $visitor);
        }
        else if ($start instanceof \SplDoublyLinkedList) {
            $queue = $start;
        }
        else if (is_object($start)) {
            $queue = new \SplDoublyLinkedList();
            $queue->push($start);
        }

        if ($queue->isEmpty()) {
            throw new RuntimeException('No start vertex or vertices were provided, and no source vertices could be found in the provided graph.', E_WARNING);
        }

        $visiting = new \SplObjectStorage();
        $visited = new \SplObjectStorage();

        $visitor->beginTraversal();

        $visit = function($vertex) use ($graph, $visitor, &$visit, $visiting, $visited) {
            if ($visiting->contains($vertex)) {
                $visitor->onBackEdge($vertex, $visit);
            }
            else if (!$visited->contains($vertex)) {
                $visiting->attach($vertex);

                $visitor->onStartVertex($vertex, $visit);

                foreach ($graph->successorsOf($vertex) as $head) {
                    $visitor->onExamineEdge($vertex, $head, $visit);
                    $visit($head);
                }

                $visitor->onFinishVertex($vertex, $visit);

                $visiting->detach($vertex);
                $visited->attach($vertex);
            }
        };

        // TODO experiment with adding a generator-producing visitor method that yields the queue here
        while (!$queue->isEmpty()) {
            $vertex = $queue->shift();
            $visit($vertex);
        }

        $visitor->endTraversal();
    }