1<?php
2/*
3 * This file is part of PHPUnit.
4 *
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11/**
12 * Utility class that can print to STDOUT or write to a file.
13 */
14class PHPUnit_Util_Printer
15{
16    /**
17     * If true, flush output after every write.
18     *
19     * @var bool
20     */
21    protected $autoFlush = false;
22
23    /**
24     * @var resource
25     */
26    protected $out;
27
28    /**
29     * @var string
30     */
31    protected $outTarget;
32
33    /**
34     * Constructor.
35     *
36     * @param mixed $out
37     *
38     * @throws PHPUnit_Framework_Exception
39     */
40    public function __construct($out = null)
41    {
42        if ($out !== null) {
43            if (is_string($out)) {
44                if (strpos($out, 'socket://') === 0) {
45                    $out = explode(':', str_replace('socket://', '', $out));
46
47                    if (count($out) != 2) {
48                        throw new PHPUnit_Framework_Exception;
49                    }
50
51                    $this->out = fsockopen($out[0], $out[1]);
52                } else {
53                    if (strpos($out, 'php://') === false &&
54                        !is_dir(dirname($out))) {
55                        mkdir(dirname($out), 0777, true);
56                    }
57
58                    $this->out = fopen($out, 'wt');
59                }
60
61                $this->outTarget = $out;
62            } else {
63                $this->out = $out;
64            }
65        }
66    }
67
68    /**
69     * Flush buffer and close output if it's not to a PHP stream
70     */
71    public function flush()
72    {
73        if ($this->out && strncmp($this->outTarget, 'php://', 6) !== 0) {
74            fclose($this->out);
75        }
76    }
77
78    /**
79     * Performs a safe, incremental flush.
80     *
81     * Do not confuse this function with the flush() function of this class,
82     * since the flush() function may close the file being written to, rendering
83     * the current object no longer usable.
84     */
85    public function incrementalFlush()
86    {
87        if ($this->out) {
88            fflush($this->out);
89        } else {
90            flush();
91        }
92    }
93
94    /**
95     * @param string $buffer
96     */
97    public function write($buffer)
98    {
99        if ($this->out) {
100            fwrite($this->out, $buffer);
101
102            if ($this->autoFlush) {
103                $this->incrementalFlush();
104            }
105        } else {
106            if (PHP_SAPI != 'cli' && PHP_SAPI != 'phpdbg') {
107                $buffer = htmlspecialchars($buffer, ENT_SUBSTITUTE);
108            }
109
110            print $buffer;
111
112            if ($this->autoFlush) {
113                $this->incrementalFlush();
114            }
115        }
116    }
117
118    /**
119     * Check auto-flush mode.
120     *
121     * @return bool
122     */
123    public function getAutoFlush()
124    {
125        return $this->autoFlush;
126    }
127
128    /**
129     * Set auto-flushing mode.
130     *
131     * If set, *incremental* flushes will be done after each write. This should
132     * not be confused with the different effects of this class' flush() method.
133     *
134     * @param bool $autoFlush
135     */
136    public function setAutoFlush($autoFlush)
137    {
138        if (is_bool($autoFlush)) {
139            $this->autoFlush = $autoFlush;
140        } else {
141            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
142        }
143    }
144}
145