1: <?php
2: /*****************************************************************************************
3: * X2Engine Open Source Edition is a customer relationship management program developed by
4: * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
5: *
6: * This program is free software; you can redistribute it and/or modify it under
7: * the terms of the GNU Affero General Public License version 3 as published by the
8: * Free Software Foundation with the addition of the following permission added
9: * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10: * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
11: * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12: *
13: * This program is distributed in the hope that it will be useful, but WITHOUT
14: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15: * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
16: * details.
17: *
18: * You should have received a copy of the GNU Affero General Public License along with
19: * this program; if not, see http://www.gnu.org/licenses or write to the Free
20: * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21: * 02110-1301 USA.
22: *
23: * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
24: * California 95067, USA. or at email address contact@x2engine.com.
25: *
26: * The interactive user interfaces in modified source and object code versions
27: * of this program must display Appropriate Legal Notices, as required under
28: * Section 5 of the GNU Affero General Public License version 3.
29: *
30: * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31: * these Appropriate Legal Notices must retain the display of the "Powered by
32: * X2Engine" logo. If the display of the logo is not reasonably feasible for
33: * technical reasons, the Appropriate Legal Notices must display the words
34: * "Powered by X2Engine".
35: *****************************************************************************************/
36:
37: /**
38: * Class to simplify web requests by abstracting creation of curl handles and stream contexts
39: */
40:
41: class RequestUtil extends CComponent {
42:
43: public $method = 'GET';
44: public $header = array ();
45: public $timeout = 5;
46: public $url;
47: public $multipart = false;
48: private $_content = '';
49: private $_header;
50:
51: public static function request ($options) {
52: return AppFileUtil::getContents (
53: Yii::createComponent (array_merge (array (
54: 'class' => get_class (),
55: ), $options)));
56: }
57:
58: public function setContent (array $content) {
59: if ($this->method === 'POST' && $this->multipart)
60: $this->_content = $content;
61: else
62: $this->_content = http_build_query ($content);
63: }
64:
65: public function getContent () {
66: return $this->_content;
67: }
68:
69: /**
70: * Magic getter to add default headers
71: */
72: public function getHeader () {
73: if (!isset ($this->_header)) {
74: $header = $this->header;
75: if ($this->method === 'POST' && $this->getContent ()) {
76: if (!isset ($header['Content-Length']) && !$this->multipart) {
77: $header['Content-Length'] = strlen ($this->getContent ());
78: }
79: if (!isset ($header['Content-Type'])) {
80: $header['Content-Type'] = 'application/x-www-form-urlencoded';
81: }
82: }
83: $this->_header = $header;
84: }
85: return $this->_header;
86: }
87:
88: /**
89: * Get stream context with specified request configuration
90: */
91: public function getStreamContext () {
92: $content = $this->getContent();
93: if ($this->multipart)
94: $content = $this->assembleMultipartContent ($content);
95:
96: if ($this->method === 'POST') {
97: } else if ($this->method === 'GET' && $this->getContent ()) {
98: $this->url .= (strpos ($this->url, '?') === false ? '?' : '&').$this->getContent ();
99: }
100: $header = array ();
101: foreach ($this->getHeader () as $name => $val) {
102: $header[] = $name.': '.$val;
103: }
104:
105: $options = array (
106: 'http' => array (
107: 'method' => $this->method,
108: 'timeout' => $this->timeout,
109: 'header' => $header,
110: 'content' => $content,
111: ));
112: if ($this->multipart)
113: $options['http']['follow_location'] = 0;
114:
115: return stream_context_create ($options);
116: }
117:
118: /**
119: * Get curl handle with specified request configuration
120: */
121: public function getCurlHandle () {
122: $ch = curl_init($this->url);
123: $curlopt = array(
124: CURLOPT_RETURNTRANSFER => 1,
125: CURLOPT_BINARYTRANSFER => 1,
126: CURLOPT_POST => $this->method === 'POST',
127: CURLOPT_TIMEOUT => $this->timeout
128: );
129: $curlopt[CURLOPT_HTTPHEADER] = $this->getHeader ();
130: if ($this->method === 'POST')
131: $curlopt[CURLOPT_POSTFIELDS] = $this->getContent ();
132:
133: curl_setopt_array($ch, $curlopt);
134: return $ch;
135: }
136:
137: /**
138: * Assemble multipart body content as demonstrated here: https://stackoverflow.com/a/4247082
139: */
140: private function assembleMultipartContent($content) {
141: $boundry = '--------------------------'.microtime(true);
142: $this->header['Content-Type'] = 'multipart/form-data; boundary='.$boundry;
143: $mpContent = '';
144: foreach ($content as $key => $value) {
145: $mpContent .= '--'.$boundry."\r\n".
146: "Content-Disposition: form-data; name=\"$key\"\r\n\r\n".
147: "$value\r\n";
148: }
149: $mpContent .= '--'.$boundry."--\r\n";
150: return $mpContent;
151: }
152: }
153:
154: ?>
155: