Completed
Pull Request — master (#412)
by Ciaran
11:24
created

ThrowablePatch   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 115
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 0
loc 115
c 0
b 0
f 0
wmc 13
lcom 1
cbo 2
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A supports() 0 4 2
A implementsAThrowableInterface() 0 4 1
A doesNotExtendAThrowableClass() 0 4 1
A containsThrowable() 0 10 3
A isThrowable() 0 4 1
A apply() 0 5 1
A checkItCanBeDoubled() 0 13 2
A setParentClassToException() 0 13 1
A getPriority() 0 4 1
1
<?php
2
3
namespace Prophecy\Doubler\ClassPatch;
4
5
use Prophecy\Doubler\Generator\Node\ClassNode;
6
use Prophecy\Exception\Doubler\ClassCreatorException;
7
8
class ThrowablePatch implements ClassPatchInterface
9
{
10
    /**
11
     * Checks if patch supports specific class node.
12
     *
13
     * @param ClassNode $node
14
     *
15
     * @return bool
16
     */
17
    public function supports(ClassNode $node)
18
    {
19
        return $this->implementsAThrowableInterface($node) && $this->doesNotExtendAThrowableClass($node);
20
    }
21
22
    /**
23
     * @param ClassNode $node
24
     * @return bool
25
     */
26
    private function implementsAThrowableInterface(ClassNode $node)
27
    {
28
        return $this->containsThrowable($node->getInterfaces());
29
    }
30
31
    /**
32
     * @param ClassNode $node
33
     * @return bool
34
     */
35
    private function doesNotExtendAThrowableClass(ClassNode $node)
36
    {
37
        return !$this->isThrowable($node->getParentClass());
38
    }
39
40
    /**
41
     * Does a list of types contain any throwables
42
     *
43
     * @param array $types
44
     * @return boolean
45
     */
46
    public function containsThrowable($types)
47
    {
48
        foreach ($types as $type) {
49
            if ($this->isThrowable($type)) {
50
                return true;
51
            }
52
        }
53
54
        return false;
55
    }
56
57
    /**
58
     * @param string $type
59
     * @return bool
60
     */
61
    private function isThrowable($type)
62
    {
63
        return is_a($type, 'Throwable', true);
64
    }
65
66
    /**
67
     * Applies patch to the specific class node.
68
     *
69
     * @param ClassNode $node
70
     *
71
     * @return void
72
     */
73
    public function apply(ClassNode $node)
74
    {
75
        $this->checkItCanBeDoubled($node);
76
        $this->setParentClassToException($node);
77
    }
78
79
    /**
80
     * @param ClassNode $node
81
     */
82
    private function checkItCanBeDoubled(ClassNode $node)
83
    {
84
        $className = $node->getParentClass();
85
        if ($className != 'stdClass') {
86
            throw new ClassCreatorException(
87
                sprintf(
88
                    'Cannot double concrete class %s as well as implement Traversable',
89
                    $className
90
                ),
91
                $node
92
            );
93
        }
94
    }
95
96
    /**
97
     * @param ClassNode $node
98
     */
99
    private function setParentClassToException(ClassNode $node)
100
    {
101
        $node->setParentClass('Exception');
102
103
        $node->removeMethod('getMessage');
104
        $node->removeMethod('getCode');
105
        $node->removeMethod('getFile');
106
        $node->removeMethod('getLine');
107
        $node->removeMethod('getTrace');
108
        $node->removeMethod('getPrevious');
109
        $node->removeMethod('getNext');
110
        $node->removeMethod('getTraceAsString');
111
    }
112
113
    /**
114
     * Returns patch priority, which determines when patch will be applied.
115
     *
116
     * @return int Priority number (higher - earlier)
117
     */
118
    public function getPriority()
119
    {
120
        return 100;
121
    }
122
}
123