1: <?php
2:
3: namespace Guzzle\Stream;
4:
5: use Guzzle\Common\Exception\InvalidArgumentException;
6: use Guzzle\Common\Exception\RuntimeException;
7: use Guzzle\Http\Message\EntityEnclosingRequestInterface;
8: use Guzzle\Http\Message\RequestInterface;
9: use Guzzle\Http\Url;
10:
11: 12: 13: 14: 15:
16: class PhpStreamRequestFactory implements StreamRequestFactoryInterface
17: {
18: 19: 20:
21: protected $context;
22:
23: 24: 25:
26: protected $contextOptions;
27:
28: 29: 30:
31: protected $url;
32:
33: 34: 35:
36: protected ;
37:
38: 39: 40: 41: 42: 43:
44: public function fromRequest(RequestInterface $request, $context = array(), array $params = array())
45: {
46: if (is_resource($context)) {
47: $this->contextOptions = stream_context_get_options($context);
48: $this->context = $context;
49: } elseif (is_array($context) || !$context) {
50: $this->contextOptions = $context;
51: $this->createContext($params);
52: } elseif ($context) {
53: throw new InvalidArgumentException('$context must be an array or resource');
54: }
55:
56: $this->setUrl($request);
57: $this->addDefaultContextOptions($request);
58: $this->addSslOptions($request);
59: $this->addBodyOptions($request);
60: $this->addProxyOptions($request);
61:
62:
63: $request->dispatch('request.before_send', array(
64: 'request' => $request,
65: 'context' => $this->context,
66: 'context_options' => $this->contextOptions
67: ));
68:
69:
70: return $this->createStream($params)
71: ->setCustomData('request', $request)
72: ->setCustomData('response_headers', $this->getLastResponseHeaders());
73: }
74:
75: 76: 77: 78: 79: 80: 81: 82:
83: protected function setContextValue($wrapper, $name, $value, $overwrite = false)
84: {
85: if (!isset($this->contextOptions[$wrapper])) {
86: $this->contextOptions[$wrapper] = array($name => $value);
87: } elseif (!$overwrite && isset($this->contextOptions[$wrapper][$name])) {
88: return;
89: }
90: $this->contextOptions[$wrapper][$name] = $value;
91: stream_context_set_option($this->context, $wrapper, $name, $value);
92: }
93:
94: 95: 96: 97: 98:
99: protected function createContext(array $params)
100: {
101: $options = $this->contextOptions;
102: $this->context = $this->createResource(function () use ($params, $options) {
103: return stream_context_create($options, $params);
104: });
105: }
106:
107: 108: 109: 110: 111:
112: public function ()
113: {
114: return $this->lastResponseHeaders;
115: }
116:
117: 118: 119: 120: 121:
122: protected function addDefaultContextOptions(RequestInterface $request)
123: {
124: $this->setContextValue('http', 'method', $request->getMethod());
125: $this->setContextValue('http', 'header', $request->getHeaderLines());
126:
127: $this->setContextValue('http', 'protocol_version', '1.0');
128: $this->setContextValue('http', 'ignore_errors', true);
129: }
130:
131: 132: 133: 134: 135:
136: protected function setUrl(RequestInterface $request)
137: {
138: $this->url = $request->getUrl(true);
139:
140:
141: if ($request->getUsername()) {
142: $this->url->setUsername($request->getUsername());
143: }
144:
145:
146: if ($request->getPassword()) {
147: $this->url->setPassword($request->getPassword());
148: }
149: }
150:
151: 152: 153: 154: 155:
156: protected function addSslOptions(RequestInterface $request)
157: {
158: if ($verify = $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)) {
159: $this->setContextValue('ssl', 'verify_peer', true, true);
160: if ($cafile = $request->getCurlOptions()->get(CURLOPT_CAINFO)) {
161: $this->setContextValue('ssl', 'cafile', $cafile, true);
162: }
163: } else {
164: $this->setContextValue('ssl', 'verify_peer', false, true);
165: }
166: }
167:
168: 169: 170: 171: 172:
173: protected function addBodyOptions(RequestInterface $request)
174: {
175:
176: if (!($request instanceof EntityEnclosingRequestInterface)) {
177: return;
178: }
179:
180: if (count($request->getPostFields())) {
181: $this->setContextValue('http', 'content', (string) $request->getPostFields(), true);
182: } elseif ($request->getBody()) {
183: $this->setContextValue('http', 'content', (string) $request->getBody(), true);
184: }
185:
186:
187: if (isset($this->contextOptions['http']['content'])) {
188: $headers = isset($this->contextOptions['http']['header']) ? $this->contextOptions['http']['header'] : array();
189: $headers[] = 'Content-Length: ' . strlen($this->contextOptions['http']['content']);
190: $this->setContextValue('http', 'header', $headers, true);
191: }
192: }
193:
194: 195: 196: 197: 198:
199: protected function addProxyOptions(RequestInterface $request)
200: {
201: if ($proxy = $request->getCurlOptions()->get(CURLOPT_PROXY)) {
202: $this->setContextValue('http', 'proxy', $proxy);
203: }
204: }
205:
206: 207: 208: 209: 210: 211: 212:
213: protected function createStream(array $params)
214: {
215: $http_response_header = null;
216: $url = $this->url;
217: $context = $this->context;
218: $fp = $this->createResource(function () use ($context, $url, &$http_response_header) {
219: return fopen((string) $url, 'r', false, $context);
220: });
221:
222:
223: if (isset($http_response_header)) {
224: $this->lastResponseHeaders = $http_response_header;
225: }
226:
227:
228: $className = isset($params['stream_class']) ? $params['stream_class'] : __NAMESPACE__ . '\\Stream';
229:
230: return new $className($fp);
231: }
232:
233: 234: 235: 236: 237: 238: 239: 240:
241: protected function createResource($callback)
242: {
243:
244: $level = error_reporting(0);
245: $resource = call_user_func($callback);
246: error_reporting($level);
247:
248:
249: if (false === $resource) {
250: $message = 'Error creating resource. ';
251: foreach (error_get_last() as $key => $value) {
252: $message .= "[{$key}] {$value} ";
253: }
254: throw new RuntimeException(trim($message));
255: }
256:
257: return $resource;
258: }
259: }
260: