1<?php
2
3/*
4 * This file is part of the Prophecy.
5 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
6 *     Marcello Duarte <marcello.duarte@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Prophecy\Doubler\ClassPatch;
13
14use Prophecy\Doubler\Generator\Node\ClassNode;
15use Prophecy\Doubler\Generator\Node\MethodNode;
16
17/**
18 * Traversable interface patch.
19 * Forces classes that implement interfaces, that extend Traversable to also implement Iterator.
20 *
21 * @author Konstantin Kudryashov <ever.zet@gmail.com>
22 */
23class TraversablePatch implements ClassPatchInterface
24{
25    /**
26     * Supports nodetree, that implement Traversable, but not Iterator or IteratorAggregate.
27     *
28     * @param ClassNode $node
29     *
30     * @return bool
31     */
32    public function supports(ClassNode $node)
33    {
34        if (in_array('Iterator', $node->getInterfaces())) {
35            return false;
36        }
37        if (in_array('IteratorAggregate', $node->getInterfaces())) {
38            return false;
39        }
40
41        foreach ($node->getInterfaces() as $interface) {
42            if ('Traversable' !== $interface && !is_subclass_of($interface, 'Traversable')) {
43                continue;
44            }
45            if ('Iterator' === $interface || is_subclass_of($interface, 'Iterator')) {
46                continue;
47            }
48            if ('IteratorAggregate' === $interface || is_subclass_of($interface, 'IteratorAggregate')) {
49                continue;
50            }
51
52            return true;
53        }
54
55        return false;
56    }
57
58    /**
59     * Forces class to implement Iterator interface.
60     *
61     * @param ClassNode $node
62     */
63    public function apply(ClassNode $node)
64    {
65        $node->addInterface('Iterator');
66
67        $node->addMethod(new MethodNode('current'));
68        $node->addMethod(new MethodNode('key'));
69        $node->addMethod(new MethodNode('next'));
70        $node->addMethod(new MethodNode('rewind'));
71        $node->addMethod(new MethodNode('valid'));
72    }
73
74    /**
75     * Returns patch priority, which determines when patch will be applied.
76     *
77     * @return int Priority number (higher - earlier)
78     */
79    public function getPriority()
80    {
81        return 100;
82    }
83}
84