1<?php
2namespace GuzzleHttp\Stream;
3
4use GuzzleHttp\Stream\Exception\SeekException;
5
6/**
7 * Static utility class because PHP's autoloaders don't support the concept
8 * of namespaced function autoloading.
9 */
10class Utils
11{
12    /**
13     * Safely opens a PHP stream resource using a filename.
14     *
15     * When fopen fails, PHP normally raises a warning. This function adds an
16     * error handler that checks for errors and throws an exception instead.
17     *
18     * @param string $filename File to open
19     * @param string $mode     Mode used to open the file
20     *
21     * @return resource
22     * @throws \RuntimeException if the file cannot be opened
23     */
24    public static function open($filename, $mode)
25    {
26        $ex = null;
27        set_error_handler(function () use ($filename, $mode, &$ex) {
28            $ex = new \RuntimeException(sprintf(
29                'Unable to open %s using mode %s: %s',
30                $filename,
31                $mode,
32                func_get_args()[1]
33            ));
34        });
35
36        $handle = fopen($filename, $mode);
37        restore_error_handler();
38
39        if ($ex) {
40            /** @var $ex \RuntimeException */
41            throw $ex;
42        }
43
44        return $handle;
45    }
46
47    /**
48     * Copy the contents of a stream into a string until the given number of
49     * bytes have been read.
50     *
51     * @param StreamInterface $stream Stream to read
52     * @param int             $maxLen Maximum number of bytes to read. Pass -1
53     *                                to read the entire stream.
54     * @return string
55     */
56    public static function copyToString(StreamInterface $stream, $maxLen = -1)
57    {
58        $buffer = '';
59
60        if ($maxLen === -1) {
61            while (!$stream->eof()) {
62                $buf = $stream->read(1048576);
63                if ($buf === false) {
64                    break;
65                }
66                $buffer .= $buf;
67            }
68            return $buffer;
69        }
70
71        $len = 0;
72        while (!$stream->eof() && $len < $maxLen) {
73            $buf = $stream->read($maxLen - $len);
74            if ($buf === false) {
75                break;
76            }
77            $buffer .= $buf;
78            $len = strlen($buffer);
79        }
80
81        return $buffer;
82    }
83
84    /**
85     * Copy the contents of a stream into another stream until the given number
86     * of bytes have been read.
87     *
88     * @param StreamInterface $source Stream to read from
89     * @param StreamInterface $dest   Stream to write to
90     * @param int             $maxLen Maximum number of bytes to read. Pass -1
91     *                                to read the entire stream.
92     */
93    public static function copyToStream(
94        StreamInterface $source,
95        StreamInterface $dest,
96        $maxLen = -1
97    ) {
98        if ($maxLen === -1) {
99            while (!$source->eof()) {
100                if (!$dest->write($source->read(1048576))) {
101                    break;
102                }
103            }
104            return;
105        }
106
107        $bytes = 0;
108        while (!$source->eof()) {
109            $buf = $source->read($maxLen - $bytes);
110            if (!($len = strlen($buf))) {
111                break;
112            }
113            $bytes += $len;
114            $dest->write($buf);
115            if ($bytes == $maxLen) {
116                break;
117            }
118        }
119    }
120
121    /**
122     * Calculate a hash of a Stream
123     *
124     * @param StreamInterface $stream    Stream to calculate the hash for
125     * @param string          $algo      Hash algorithm (e.g. md5, crc32, etc)
126     * @param bool            $rawOutput Whether or not to use raw output
127     *
128     * @return string Returns the hash of the stream
129     * @throws SeekException
130     */
131    public static function hash(
132        StreamInterface $stream,
133        $algo,
134        $rawOutput = false
135    ) {
136        $pos = $stream->tell();
137
138        if ($pos > 0 && !$stream->seek(0)) {
139            throw new SeekException($stream);
140        }
141
142        $ctx = hash_init($algo);
143        while (!$stream->eof()) {
144            hash_update($ctx, $stream->read(1048576));
145        }
146
147        $out = hash_final($ctx, (bool) $rawOutput);
148        $stream->seek($pos);
149
150        return $out;
151    }
152
153    /**
154     * Read a line from the stream up to the maximum allowed buffer length
155     *
156     * @param StreamInterface $stream    Stream to read from
157     * @param int             $maxLength Maximum buffer length
158     * @param string          $eol       Line ending
159     *
160     * @return string|bool
161     */
162    public static function readline(StreamInterface $stream, $maxLength = null, $eol = PHP_EOL)
163    {
164        $buffer = '';
165        $size = 0;
166        $negEolLen = -strlen($eol);
167
168        while (!$stream->eof()) {
169            if (false === ($byte = $stream->read(1))) {
170                return $buffer;
171            }
172            $buffer .= $byte;
173            // Break when a new line is found or the max length - 1 is reached
174            if (++$size == $maxLength || substr($buffer, $negEolLen) === $eol) {
175                break;
176            }
177        }
178
179        return $buffer;
180    }
181
182    /**
183     * Alias of GuzzleHttp\Stream\Stream::factory.
184     *
185     * @param mixed $resource Resource to create
186     * @param array $options  Associative array of stream options defined in
187     *                        {@see \GuzzleHttp\Stream\Stream::__construct}
188     *
189     * @return StreamInterface
190     *
191     * @see GuzzleHttp\Stream\Stream::factory
192     * @see GuzzleHttp\Stream\Stream::__construct
193     */
194    public static function create($resource, array $options = [])
195    {
196        return Stream::factory($resource, $options);
197    }
198}
199