Completed
Push — master ( 2e5bdd...9e2471 )
by Ryuichi
05:26
created

FileOutputStream::__construct()   D

Complexity

Conditions 9
Paths 20

Size

Total Lines 33
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
dl 0
loc 33
ccs 0
cts 29
cp 0
rs 4.909
c 0
b 0
f 0
cc 9
crap 90
eloc 22
nc 20
nop 2
1
<?php
2
namespace WebStream\IO;
3
4
use WebStream\IO\OutputStream;
5
use WebStream\Exception\Extend\InvalidArgumentException;
6
use WebStream\Exception\Extend\IOException;
7
8
/**
9
 * FileOutputStream
10
 * @author Ryuichi TANAKA.
11
 * @since 2016/02/24
12
 * @version 0.7
13
 */
14
class FileOutputStream extends OutputStream
15
{
16
    /**
17
     * @var File ファイルオブジェクト
18
     */
19
    protected $file;
20
21
    /**
22
     * constructor
23
     * @param mixed $file ファイルオブジェクトまたはファイルパス
24
     * @param bool $isAppend 追記フラグ
25
     * @throws InvalidArgumentException
26
     * @throws IOException
27
     */
28
    public function __construct($file, bool $isAppend = false)
29
    {
30
        $filepath = null;
31
        if ($file instanceof File) {
32
            $this->file = $file;
33
            $filepath = $this->file->getFilePath();
34
        } elseif (is_string($file)) {
35
            if (!file_exists($file)) {
36
                $dirname = dirname($file);
37
                $dir = new File($dirname);
38
                if (!$dir->isWritable()) {
39
                    throw new IOException("Cannot writable: " . $dirname);
40
                }
41
            }
42
            $this->file = new File($file);
43
            $filepath = $this->file->getFilePath();
44
        } else {
45
            throw new InvalidArgumentException("Invalid argument type: " . $file);
46
        }
47
48
        $mode = $isAppend ? 'ab' : 'wb';
49
        $stream = fopen($filepath, $mode);
50
51
        if (!is_resource($stream) || $stream === false) {
52
            throw new IOException("Unable open " . $this->file->getFilePath());
53
        }
54
55
        if (!flock($stream, LOCK_EX | LOCK_NB)) {
56
            throw new IOException("Cannot lock file: " . $this->file->getFilePath());
57
        }
58
59
        parent::__construct($stream);
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65
    public function write($buf, int $off = null, int $len = null)
66
    {
67
        $data = null;
68
        if ($off === null && $len === null) {
69
            $data = $buf;
70
        } elseif ($off !== null && $len === null) {
71
            $data = substr($buf, $off);
72
        } elseif ($off === null && $len !== null) {
73
            $data = substr($buf, 0, $len);
74
        } else {
75
            $data = substr($buf, $off, $len);
76
        }
77
78
        if (@fwrite($this->stream, $data) === false) {
79
            throw new IOException("Failed to write stream.");
80
        }
81
    }
82
83
    /**
84
     * {@inheritdoc}
85
     */
86
    public function close()
87
    {
88
        if ($this->stream === null) {
89
            return;
90
        }
91
92
        $this->flush();
93
94
        // PHP5.3.2以降はfcloseではロック解放されなくなり、明示的に開放する必要がある
95
        flock($this->stream, LOCK_UN);
96
97
        if (get_resource_type($this->stream) !== 'Unknown' && fclose($this->stream) === false) {
98
            throw new IOException("Cannot close output stream.");
99
        }
100
101
        $this->stream = null;
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107
    public function flush()
108
    {
109
        if (@fflush($this->stream) === false) {
110
            throw new IOException("Failed to flush.");
111
        }
112
    }
113
}
114