1<?php
2
3namespace GuzzleHttp\Psr7;
4
5use Psr\Http\Message\StreamInterface;
6
7/**
8 * Stream decorator trait
9 *
10 * @property StreamInterface stream
11 */
12trait StreamDecoratorTrait
13{
14    /**
15     * @param StreamInterface $stream Stream to decorate
16     */
17    public function __construct(StreamInterface $stream)
18    {
19        $this->stream = $stream;
20    }
21
22    /**
23     * Magic method used to create a new stream if streams are not added in
24     * the constructor of a decorator (e.g., LazyOpenStream).
25     *
26     * @param string $name Name of the property (allows "stream" only).
27     *
28     * @return StreamInterface
29     */
30    public function __get($name)
31    {
32        if ($name == 'stream') {
33            $this->stream = $this->createStream();
34            return $this->stream;
35        }
36
37        throw new \UnexpectedValueException("$name not found on class");
38    }
39
40    public function __toString()
41    {
42        try {
43            if ($this->isSeekable()) {
44                $this->seek(0);
45            }
46            return $this->getContents();
47        } catch (\Exception $e) {
48            // Really, PHP? https://bugs.php.net/bug.php?id=53648
49            trigger_error('StreamDecorator::__toString exception: '
50                . (string) $e, E_USER_ERROR);
51            return '';
52        }
53    }
54
55    public function getContents()
56    {
57        return Utils::copyToString($this);
58    }
59
60    /**
61     * Allow decorators to implement custom methods
62     *
63     * @param string $method Missing method name
64     * @param array  $args   Method arguments
65     *
66     * @return mixed
67     */
68    public function __call($method, array $args)
69    {
70        $result = call_user_func_array([$this->stream, $method], $args);
71
72        // Always return the wrapped object if the result is a return $this
73        return $result === $this->stream ? $this : $result;
74    }
75
76    public function close()
77    {
78        $this->stream->close();
79    }
80
81    public function getMetadata($key = null)
82    {
83        return $this->stream->getMetadata($key);
84    }
85
86    public function detach()
87    {
88        return $this->stream->detach();
89    }
90
91    public function getSize()
92    {
93        return $this->stream->getSize();
94    }
95
96    public function eof()
97    {
98        return $this->stream->eof();
99    }
100
101    public function tell()
102    {
103        return $this->stream->tell();
104    }
105
106    public function isReadable()
107    {
108        return $this->stream->isReadable();
109    }
110
111    public function isWritable()
112    {
113        return $this->stream->isWritable();
114    }
115
116    public function isSeekable()
117    {
118        return $this->stream->isSeekable();
119    }
120
121    public function rewind()
122    {
123        $this->seek(0);
124    }
125
126    public function seek($offset, $whence = SEEK_SET)
127    {
128        $this->stream->seek($offset, $whence);
129    }
130
131    public function read($length)
132    {
133        return $this->stream->read($length);
134    }
135
136    public function write($string)
137    {
138        return $this->stream->write($string);
139    }
140
141    /**
142     * Implement in subclasses to dynamically create streams when requested.
143     *
144     * @return StreamInterface
145     *
146     * @throws \BadMethodCallException
147     */
148    protected function createStream()
149    {
150        throw new \BadMethodCallException('Not implemented');
151    }
152}
153