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