Completed
Push — master ( 8b71eb...ddd45f )
by Maik
09:34
created

MemoryStream::isOpen()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
/**
4
 * This file is part of the PHP Generics package.
5
 *
6
 * @package Generics
7
 */
8
namespace Generics\Streams;
9
10
use Generics\Resettable;
11
use Generics\Util\Interpolator;
12
13
/**
14
 * This class provides a memory stream for both input and output
15
 *
16
 * @author Maik Greubel <[email protected]>
17
 */
18
class MemoryStream implements InputOutputStream, Resettable
19
{
20
    use Interpolator {
21
        interpolate as tinterpolate;
22
    }
23
    /**
24
     * The local memory buffer
25
     *
26
     * @var string
27
     */
28
    private $memory;
29
30
    /**
31
     * Current position in memory buffer
32
     *
33
     * @var int
34
     */
35
    private $current;
36
37
    /**
38
     * Whether it is possible to perform reading action
39
     *
40
     * @var boolean
41
     */
42
    private $ready;
43
44
    /**
45
     * Whether stream is closed
46
     *
47
     * @var boolean
48
     */
49
    private $closed;
50
51
    /**
52
     * Create a new MemoryStream
53
     *
54
     * @param InputStream $in
55
     *            optional existing input stream - will be copied
56
     */
57 31
    public function __construct(InputStream $in = null)
58
    {
59 31
        $this->memory = "";
60 31
        if ($in != null) {
61 1
            $copy = clone $in;
62 1
            $copy->reset();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Generics\Streams\InputStream as the method reset() does only exist in the following implementations of said interface: Generics\Streams\FileInputStream, Generics\Streams\MemoryStream.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
63 1
            while ($copy->ready()) {
64 1
                $this->memory .= $copy->read();
65 1
            }
66 1
            $copy->close();
67 1
        }
68 31
        $this->current = 0;
69 31
        $this->ready = true;
70 31
        $this->closed = false;
71 31
    }
72
73
    /**
74
     * {@inheritDoc}
75
     * @see \Generics\Streams\Stream::close()
76
     */
77
    public function close()
78 5
    {
79
        unset($this->memory);
80 5
        $this->current = 0;
81 5
        $this->ready = false;
82 5
        $this->closed = true;
83 5
    }
84 5
85
    /**
86
     * {@inheritDoc}
87
     * @see \Generics\Streams\Stream::ready()
88
     */
89
    public function ready()
90
    {
91 21
        return $this->ready;
92
    }
93 21
94
    /**
95
     * {@inheritDoc}
96
     * @see \Generics\Streams\OutputStream::write()
97
     */
98
    public function write($buffer)
99
    {
100
        if ($this->closed) {
101 29
            throw new StreamException("Stream is not open");
102
        }
103 29
        $this->memory .= $buffer;
104 1
        $this->ready = true;
105
    }
106 28
107 28
    /**
108 28
     * {@inheritDoc}
109
     * @see \Generics\Streams\InputStream::read()
110
     */
111
    public function read($length = 1, $offset = null)
112
    {
113
        if ($this->closed) {
114
            throw new StreamException("Stream is not open");
115 24
        }
116
117 24
        if ($offset !== null) {
118 1
            $this->current = intval($offset);
119
        }
120
121 23
        if (strlen($this->memory) <= $this->current) {
122
            $this->ready = false;
123
            return "";
124
        }
125 23
126 7
        if (strlen($this->memory) - $this->current < $length) {
127 7
            $length = strlen($this->memory) - $this->current;
128
        }
129
130 23
        $out = substr($this->memory, $this->current, $length);
131 23
        $this->current += $length;
132 23
133
        if ($this->current == strlen($this->memory)) {
134 23
            $this->ready = false;
135 23
        }
136
137 23
        return $out;
138 23
    }
139 23
140
    /**
141 23
     * {@inheritDoc}
142
     * @see \Countable::count()
143
     */
144
    public function count()
145
    {
146
        if ($this->closed) {
147
            throw new StreamException("Stream is not open");
148
        }
149 10
        if (!isset($this->memory)) {
150
            return 0;
151 10
        }
152 1
        return strlen($this->memory);
153
    }
154 9
155 1
    /**
156
     * {@inheritDoc}
157 8
     * @see \Generics\Resettable::reset()
158
     */
159
    public function reset()
160
    {
161
        if ($this->closed) {
162
            throw new StreamException("Stream is not open");
163
        }
164
        $this->current = 0;
165 10
        $this->ready = true;
166
    }
167 10
168 1
    /**
169
     * Write to stream by interpolation of context vars into a string
170 9
     *
171 9
     * @param string $string
172 9
     *            The string to interpolate, may contains placeholders in format {placeholder}.
173
     * @param array $context
174
     *            The context array containing the associative replacers and its values.
175
     */
176
    public function interpolate($string, array $context)
177
    {
178
        $this->write($this->tinterpolate($string, $context));
179
    }
180
181
    /**
182 20
     * {@inheritDoc}
183
     * @see \Generics\Streams\OutputStream::isWriteable()
184 20
     */
185 20
    public function isWriteable()
186
    {
187
        return true;
188
    }
189
190
    /**
191
     * {@inheritDoc}
192 1
     * @see \Generics\Streams\OutputStream::flush()
193
     */
194 1
    public function flush()
195
    {
196
        if ($this->closed) {
197
            throw new StreamException("Stream is not open");
198
        }
199
200
        unset($this->memory);
201
        $this->reset();
202 2
    }
203
    
204 2
    /**
205 1
     * {@inheritDoc}
206
     * @see \Generics\Streams\Stream::isOpen()
207
     */
208 1
    public function isOpen()
209 1
    {
210 1
    	return true;
211
    }
212
}
213