Completed
Push — master ( 64bd2a...23c640 )
by thomas
16:50 queued 01:51
created

Buffer::int()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 1
Metric Value
c 4
b 0
f 1
dl 0
loc 6
rs 9.4285
cc 2
eloc 4
nc 2
nop 3
1
<?php
2
3
namespace BitWasp\Buffertools;
4
5
use Mdanter\Ecc\EccFactory;
6
use Mdanter\Ecc\Math\GmpMathInterface;
7
8
class Buffer implements BufferInterface
9
{
10
    /**
11
     * @var int
12
     */
13
    protected $size;
14
15
    /**
16
     * @var string
17
     */
18
    protected $buffer;
19
20
    /**
21
     * @var GmpMathInterface
22
     */
23
    protected $math;
24
25
    /**
26
     * @param string               $byteString
27
     * @param null|integer         $byteSize
28
     * @param GmpMathInterface     $math
29
     * @throws \Exception
30
     */
31
    public function __construct($byteString = '', $byteSize = null, GmpMathInterface $math = null)
32
    {
33
        $this->math = $math ?: EccFactory::getAdapter();
34
        if ($byteSize !== null) {
35
            // Check the integer doesn't overflow its supposed size
36
            if (strlen($byteString) > $byteSize) {
37
                throw new \Exception('Byte string exceeds maximum size');
38
            }
39
        } else {
40
            $byteSize = strlen($byteString);
41
        }
42
43
        $this->size   = $byteSize;
44
        $this->buffer = $byteString;
45
    }
46
47
    /**
48
     * Return a formatted version for var_dump
49
     */
50
    public function __debugInfo()
51
    {
52
        return [
53
            'size' => $this->size,
54
            'buffer' => '0x' . unpack("H*", $this->buffer)[1],
55
        ];
56
    }
57
58
    /**
59
     * Create a new buffer from a hex string
60
     *
61
     * @param string $hexString
62
     * @param integer $byteSize
63
     * @param GmpMathInterface $math
64
     * @return Buffer
65
     * @throws \Exception
66
     */
67
    public static function hex($hexString = '', $byteSize = null, GmpMathInterface $math = null)
68
    {
69
        if (strlen($hexString) > 0 && !ctype_xdigit($hexString)) {
70
            throw new \InvalidArgumentException('BufferHex: non-hex character passed: ' . $hexString);
71
        }
72
73
        $math = $math ?: EccFactory::getAdapter();
74
        $binary = pack("H*", $hexString);
75
        return new self($binary, $byteSize, $math);
76
    }
77
78
    /**
79
     * @param int|string $integer
80
     * @param null|int $byteSize
81
     * @param GmpMathInterface|null $math
82
     * @return Buffer
83
     */
84
    public static function int($integer, $byteSize = null, GmpMathInterface $math = null)
85
    {
86
        $math = $math ?: EccFactory::getAdapter();
87
        $binary = pack("H*", $math->decHex($integer));
88
        return new self($binary, $byteSize, $math);
89
    }
90
91
    /**
92
     * @param integer      $start
93
     * @param integer|null $end
94
     * @return Buffer
95
     * @throws \Exception
96
     */
97
    public function slice($start, $end = null)
98
    {
99
        if ($start > $this->getSize()) {
100
            throw new \Exception('Start exceeds buffer length');
101
        }
102
103
        if ($end === null) {
104
            return new self(substr($this->getBinary(), $start));
105
        }
106
107
        if ($end > $this->getSize()) {
108
            throw new \Exception('Length exceeds buffer length');
109
        }
110
111
        $string = substr($this->getBinary(), $start, $end);
112
        $length = strlen($string);
113
        return new self($string, $length, $this->math);
114
    }
115
116
    /**
117
     * Get the size of the buffer to be returned
118
     *
119
     * @return int
120
     */
121
    public function getSize()
122
    {
123
        return $this->size;
124
    }
125
126
    /**
127
     * Get the size of the value stored in the buffer
128
     *
129
     * @return int
130
     */
131
    public function getInternalSize()
132
    {
133
        return strlen($this->buffer);
134
    }
135
136
    /**
137
     * @return string
138
     */
139
    public function getBinary()
140
    {
141
        // if a size is specified we'll make sure the value returned is that size
142
        if ($this->size !== null) {
143
            if (strlen($this->buffer) < $this->size) {
144
                return str_pad($this->buffer, $this->size, chr(0), STR_PAD_LEFT);
145
            } elseif (strlen($this->buffer) > $this->size) {
146
                return substr($this->buffer, 0, $this->size);
147
            }
148
        }
149
150
        return $this->buffer;
151
    }
152
153
    /**
154
     * @return string
155
     */
156
    public function getHex()
157
    {
158
        return unpack("H*", $this->getBinary())[1];
159
    }
160
161
    /**
162
     * @return \GMP
163
     */
164
    public function getGmp()
165
    {
166
        $gmp = gmp_init($this->getHex(), 16);
167
        return $gmp;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $gmp; (resource) is incompatible with the return type declared by the interface BitWasp\Buffertools\BufferInterface::getGmp of type GMP.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

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

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
168
    }
169
170
    /**
171
     * @return int|string
172
     */
173
    public function getInt()
174
    {
175
        return gmp_strval($this->getGmp(), 10);
176
    }
177
178
    /**
179
     * @return Buffer
180
     */
181
    public function flip()
182
    {
183
        return Buffertools::flipBytes($this);
0 ignored issues
show
Bug Compatibility introduced by
The expression \BitWasp\Buffertools\Buf...ools::flipBytes($this); of type BitWasp\Buffertools\Buffer|string adds the type string to the return on line 183 which is incompatible with the return type declared by the interface BitWasp\Buffertools\BufferInterface::flip of type BitWasp\Buffertools\Buffer.
Loading history...
184
    }
185
186
    /**
187
     * @param BufferInterface $other
188
     * @return bool
189
     */
190
    public function equals(BufferInterface $other)
191
    {
192
        return ($other->getSize() === $this->getSize()
193
             && $other->getBinary() === $this->getBinary());
194
    }
195
}
196