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

FileOutputStream::isOpen()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
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\FileExistsException;
11
use Generics\NoAccessException;
12
use Generics\LockException;
13
use Generics\Lockable;
14
15
/**
16
 * This class provides a file output stream.
17
 *
18
 * @author Maik
19
 */
20
class FileOutputStream implements OutputStream, Lockable
21
{
22
23
    /**
24
     * The file handle
25
     *
26
     * @var resource
27
     */
28
    private $handle;
29
30
    /**
31
     * The absolute file path and name
32
     *
33
     * @var string
34
     */
35
    private $fileName;
36
37
    /**
38
     * Whether resource is locked
39
     *
40
     * @var boolean
41
     */
42
    private $locked;
43
44
    /**
45
     * Create a new FileOutputStream
46
     *
47
     * @param string $file
48
     *            The absolute (or relative) path to file to write into.
49
     * @param boolean $append
50
     *            Whether to append the data to an existing file.
51
     * @throws FileExistsException will be thrown in case of file exists and append is set to false.
52
     * @throws NoAccessException will be thrown in case of it is not possible to write to file.
53
     */
54 24
    public function __construct($file, $append = false)
55
    {
56 24
        $this->locked = false;
57
58 24
        $mode = "wb";
59
60 24
        if (file_exists($file)) {
61 3
            if (! $append) {
62 1
                throw new FileExistsException("File $file already exists!");
63
            }
64
65 2
            if (! is_writable($file)) {
66
                throw new NoAccessException("Cannot write to file $file");
67
            }
68 2
            $mode = "ab";
69 2
        } else {
70 22
            if (! is_writable(dirname($file))) {
71
                throw new NoAccessException("Cannot write to file {file}", array(
72
                    'file' => $file
73
                ));
74
            }
75
        }
76
77 23
        $this->handle = fopen($file, $mode);
78
79 23
        if (! $this->ready()) {
80
            throw new StreamException("Could not open {file} for writing", array(
81
                'file' => $file
82
            ));
83
        }
84
85 23
        $this->fileName = $file;
86 23
    }
87
88
    /**
89
     * Cleanup (e.g. release lock)
90
     */
91 23
    public function __destruct()
92
    {
93
        try {
94 23
            if ($this->locked) {
95 2
                $this->unlock();
96 2
            }
97 23
        } catch (\Generics\GenericsException $ex) {
98
            // Do nothing
99
        }
100 23
    }
101
102
    /**
103
     * {@inheritDoc}
104
     * @see \Generics\Streams\Stream::ready()
105
     */
106
    public function ready()
107 23
    {
108
        return is_resource($this->handle);
109 23
    }
110
111
    /**
112
     * {@inheritDoc}
113
     * @see \Generics\Streams\OutputStream::write()
114
     */
115
    public function write($buffer)
116
    {
117 18
        if (! $this->ready()) {
118
            throw new StreamException("Stream is not open!");
119 18
        }
120
121
        if ($buffer instanceof InputStream) {
122
            $in = clone $buffer;
123 18
            assert($in instanceof InputStream);
124 14
            while ($in->ready()) {
125 14
                $buf = $in->read(1024);
126 14
                if (fwrite($this->handle, $buf) != strlen($buf)) {
127 14
                    throw new StreamException("Could not write memory stream into file");
128 14
                }
129
            }
130
        } else {
131 14
            $buffer = strval($buffer);
132 14
            if (fwrite($this->handle, $buffer) != strlen($buffer)) {
133 4
                throw new StreamException("Could not write buffer into file");
134 4
            }
135
            fflush($this->handle);
136
        }
137 4
    }
138
139 18
    /**
140
     * {@inheritDoc}
141
     * @see \Generics\Streams\Stream::close()
142
     */
143
    public function close()
144
    {
145
        if (is_resource($this->handle)) {
146 20
            fclose($this->handle);
147
            $this->handle = null;
148 20
        }
149 20
    }
150 20
151 20
    /**
152 20
     * {@inheritDoc}
153
     * @see \Countable::count()
154
     */
155
    public function count()
156
    {
157
        if (! $this->ready()) {
158
            throw new StreamException("Stream is not open!");
159 2
        }
160
161 2
        return ftell($this->handle);
162 1
    }
163
164
    /**
165 1
     * Retrieve the file path and name
166
     *
167
     * @return string
168
     */
169
    public function __toString()
170
    {
171
        return realpath($this->fileName);
172
    }
173 1
174
    /**
175 1
     * {@inheritDoc}
176
     * @see \Generics\Streams\OutputStream::isWriteable()
177
     */
178
    public function isWriteable()
179
    {
180
        return $this->ready();
181
    }
182
183 1
    /**
184
     * {@inheritDoc}
185 1
     * @see \Generics\Streams\OutputStream::flush()
186
     */
187
    public function flush()
188
    {
189
        if (! $this->ready()) {
190
            throw new StreamException("Stream is not open!");
191
        }
192
193 15
        fflush($this->handle);
194
    }
195 15
196 1
    /**
197
     * {@inheritDoc}
198
     * @see \Generics\Lockable::lock()
199 14
     */
200 14 View Code Duplication
    public function lock()
201
    {
202
        if ($this->locked || flock($this->handle, LOCK_EX) === false) {
203
            throw new LockException("Could not acquire lock");
204
        }
205
        $this->locked = true;
206
    }
207 3
208
    /**
209 3
     * {@inheritDoc}
210 1
     * @see \Generics\Lockable::unlock()
211
     */
212 3 View Code Duplication
    public function unlock()
213 3
    {
214
        if (!$this->locked || flock($this->handle, LOCK_UN) === false) {
215
            throw new LockException("Could not release lock");
216
        }
217
        $this->locked = false;
218
    }
219
    
220 4
    /**
221
     * {@inheritDoc}
222 4
     * @see \Generics\Lockable::isLocked()
223 1
     */
224
    public function isLocked()
225 3
    {
226 3
    	return $this->locked;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->locked; (boolean) is incompatible with the return type declared by the interface Generics\Lockable::isLocked of type Generics\true.

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...
227
    }
228
229
    /**
230
     * {@inheritDoc}
231
     * @see \Generics\Streams\Stream::isOpen()
232
     */
233
    public function isOpen()
234
    {
235
    	return is_resource($this->handle);
236
    }
237
}
238