Completed
Pull Request — master (#191)
by thomas
74:56 queued 71:16
created

Stack   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 114
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 90%

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 16
c 2
b 0
f 1
lcom 1
cbo 0
dl 0
loc 114
ccs 45
cts 50
cp 0.9
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A typeCheck() 0 6 2
A offsetGet() 0 5 1
A offsetSet() 0 6 1
A offsetExists() 0 5 1
A offsetUnset() 0 5 1
A swap() 0 7 1
B add() 0 21 6
A end() 0 9 2
1
<?php
2
3
namespace BitWasp\Bitcoin\Script\Interpreter;
4
5
use BitWasp\Buffertools\Buffer;
6
7
class Stack extends \SplDoublyLinkedList
8
{
9 1548
    public function __construct()
10
    {
11 1548
        $this->setIteratorMode(\SplDoublyLinkedList::IT_MODE_FIFO | \SplDoublyLinkedList::IT_MODE_FIFO);
12 1548
    }
13
14
    /**
15
     * @param mixed $value
16
     * @throws \InvalidArgumentException
17
     */
18 54
    private function typeCheck($value)
19
    {
20 54
        if (!$value instanceof Buffer) {
21
            throw new \InvalidArgumentException('Value was not of type Buffer');
22
        }
23 54
    }
24
25
    /**
26
     * @see \ArrayAccess::offsetGet()
27
     * @param int $offset
28
     * @return \BitWasp\Buffertools\Buffer
29
     */
30 500
    public function offsetGet($offset)
31
    {
32 500
        $offset = count($this) + $offset;
33 500
        return parent::offsetGet($offset);
34
    }
35
36
    /**
37
     * @see \ArrayAccess::offsetSet()
38
     * @param int $offset
39
     * @param Buffer $value
40
     * @throws \InvalidArgumentException
41
     */
42 36
    public function offsetSet($offset, $value)
43
    {
44 36
        $this->typeCheck($value);
45 36
        $offset = count($this) + $offset;
46 36
        parent::offsetSet($offset, $value);
47 36
    }
48
49
    /**
50
     * @see \ArrayAccess::offsetExists()
51
     * @param int $offset
52
     * @return bool
53
     */
54 4
    public function offsetExists($offset)
55
    {
56 4
        $offset = count($this) + $offset;
57 4
        return parent::offsetExists($offset);
58
    }
59
60
    /**
61
     * @see \ArrayAccess::offsetUnset()
62
     * @param int $offset
63
     */
64 42
    public function offsetUnset($offset)
65
    {
66 42
        $offset = count($this) + $offset;
67 42
        parent::offsetUnset($offset);
68 36
    }
69
70
    /**
71
     * @param int $first
72
     * @param int $second
73
     */
74 24
    public function swap($first, $second)
75
    {
76 24
        $val1 = $this->offsetGet($first);
77 24
        $val2 = $this->offsetGet($second);
78 24
        $this->offsetSet($second, $val1);
79 24
        $this->offsetSet($first, $val2);
80 24
    }
81
82
    /**
83
     * @param int $index
84
     * @param Buffer $value
85
     */
86 18
    public function add($index, $value)
87
    {
88 18
        $this->typeCheck($value);
89
90 18
        if (getenv('HHVM_VERSION') || version_compare(phpversion(), '5.5.0', 'lt')) {
91 6
            $size = count($this);
92 6
            $temp = [];
93 6
            for ($i = $size; $i >= $index; $i++) {
94 6
                $value = $this->pop();
95 4
                if ($i !== $index) {
96 4
                    array_unshift($temp, $value);
97 4
                }
98 4
            }
99
100
            foreach ($temp as $value) {
101
                $this->push($value);
102
            }
103
        } else {
104 12
            parent::add($index, $value);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class SplDoublyLinkedList as the method add() does only exist in the following sub-classes of SplDoublyLinkedList: BitWasp\Bitcoin\Script\Interpreter\Stack. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
105
        }
106 8
    }
107
108
    /**
109
     * @return int
110
     */
111 1392
    public function end()
112
    {
113 1392
        $count = count($this);
114 1392
        if ($count === 0) {
115 1392
            return 0;
116
        }
117
118 12
        return $count - 1;
119
    }
120
}
121