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

FileInputStream::isLocked()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
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\FileNotFoundException;
11
use \Generics\Resettable;
12
use \Generics\Lockable;
13
use \Generics\LockException;
14
15
/**
16
 * This class provides an input stream for files.
17
 *
18
 * @author Maik Greubel <[email protected]>
19
 */
20
class FileInputStream implements InputStream, Resettable, 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 the access is locked
39
     *
40
     * @var boolean
41
     */
42
    private $locked;
43
44
    /**
45
     * Create a new FileInputStream
46
     *
47
     * @param string $file
48
     *            The absolute (or relative) path to the file to open
49
     * @throws FileNotFoundException
50
     */
51 23
    public function __construct($file)
52
    {
53 23
        if (! file_exists($file)) {
54 1
            throw new FileNotFoundException("File {file} could not be found", array(
55
                'file' => $file
56 1
            ));
57
        }
58
59 22
        $this->handle = fopen($file, "rb");
60
61 22
        if (! $this->ready()) {
62
            throw new StreamException("Could not open {file} for reading", array(
63
                'file' => $file
64
            ));
65
        }
66
67 22
        $this->fileName = $file;
68 22
    }
69
70
    /**
71
     * Cleanup (e.g. release lock)
72
     */
73 22
    public function __destruct()
74
    {
75
        try {
76 22
            if ($this->locked) {
77 1
                $this->unlock();
78 1
            }
79 22
        } catch (\Generics\GenericsException $ex) {
80
            // Do nothing
81
        }
82 22
    }
83
84
    /**
85
     * {@inheritDoc}
86
     * @see \Generics\Streams\Stream::close()
87
     */
88
    public function close()
89 16
    {
90
        if ($this->handle != null) {
91 16
            fclose($this->handle);
92 16
            $this->handle = null;
93 16
        }
94 16
    }
95 16
96
    /**
97
     * {@inheritDoc}
98
     * @see \Generics\Streams\Stream::ready()
99
     */
100
    public function ready()
101
    {
102 22
        return is_resource($this->handle) && ! feof($this->handle);
103
    }
104 22
105
    /**
106
     * {@inheritDoc}
107
     * @see \Generics\Streams\InputStream::read()
108
     */
109
    public function read($length = 1, $offset = null)
110
    {
111
        if (! $this->ready()) {
112 18
            throw new StreamException("Stream is not ready!");
113
        }
114 18
115 1
        if ($offset !== null && intval($offset) > 0) {
116
            if (fseek($this->handle, $offset, SEEK_SET) != 0) {
117
                throw new StreamException("Could not set offset!");
118 17
            }
119
        }
120
121
        return fread($this->handle, $length);
122
    }
123
124 17
    /**
125
     * {@inheritDoc}
126
     * @see \Countable::count()
127
     */
128
    public function count()
129
    {
130
        $stat = fstat($this->handle);
131
        return $stat['size'];
132 3
    }
133
134 3
    /**
135 3
     *{@inheritDoc}
136
     * @see \Generics\Streams\InputStream::reset()
137
     */
138
    public function reset()
139
    {
140
        fseek($this->handle, 0, SEEK_SET);
141
    }
142
143 2
    /**
144
     * {@inheritDoc}
145 2
     * @see \Generics\Lockable::lock()
146 2
     */
147 View Code Duplication
    public function lock()
148
    {
149
        if ($this->locked || flock($this->handle, LOCK_SH) === false) {
150
            throw new LockException("Could not acquire lock");
151
        }
152
        $this->locked = true;
153 2
    }
154
155 2
    /**
156 1
     * {@inheritDoc}
157
     * @see \Generics\Lockable::unlock()
158 2
     */
159 2 View Code Duplication
    public function unlock()
160
    {
161
        if (!$this->locked || flock($this->handle, LOCK_UN) === false) {
162
            throw new LockException("Could not release lock");
163
        }
164
        $this->locked = false;
165
    }
166 2
    
167
    /**
168 2
     * {@inheritDoc}
169
     * @see \Generics\Lockable::isLocked()
170
     */
171 2
    public function isLocked()
172 2
    {
173
    	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...
174
    }
175
176
    /**
177
     * Retrieve the file path and name
178
     *
179
     * @return string
180
     */
181
    public function __toString()
182
    {
183
        return realpath($this->fileName);
184
    }
185
    
186
    /**
187
     * {@inheritDoc}
188
     * @see \Generics\Streams\Stream::isOpen()
189
     */
190
    public function isOpen()
191
    {
192
    	return is_resource($this->handle);
193
    }
194
}
195