diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..64c7cfd --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +### Composer template +composer.phar +vendor/ +composer.lock +.idea + +# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file +# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file +# composer.lock diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..28d2c41 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Alejandro Labrada Diaz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..346b72f --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Soap Client using Curl [![version][packagist-version]][packagist-url] + +[![Downloads][packagist-downloads]][packagist-url] +[![Dependencies][versioneye-image]][versioneye-url] +[![License][packagist-license]][license-url] diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..c740caa --- /dev/null +++ b/composer.json @@ -0,0 +1,27 @@ +{ + "name": "aleplusplus/soapclient-curl-php", + "description": "Soap Client using Curl", + "keywords": ["soap", "curl", "non-wsdl", "client", "sri"], + "type": "library", + "require": { + "php": ">=5.4.0", + "ext-curl": "*", + "ext-json": "*" + }, + "license": "MIT", + "authors": [ + { + "name": "Alejandro Labrada Diaz", + "email": "aleplusplus@gmail.com" + } + ], + "minimum-stability": "dev", + "autoload": { + "psr-0": { + "SoapClientCurl": "src" + } + }, + "support": { + "email": "aleplusplus@gmail.com" + } +} diff --git a/src/SoapClientCurl.php b/src/SoapClientCurl.php new file mode 100644 index 0000000..03a0d42 --- /dev/null +++ b/src/SoapClientCurl.php @@ -0,0 +1,4 @@ + '', + 'pass' => '', + 'method' => CURLAUTH_BASIC + ); + private static $proxy = array( + 'port' => false, + 'tunnel' => false, + 'address' => false, + 'type' => CURLPROXY_HTTP, + 'auth' => array ( + 'user' => '', + 'pass' => '', + 'method' => CURLAUTH_BASIC + ) + ); + + /** + * Send a cURL request + * @param string $url URL to send the request to + * @param mixed $body request body + * @param array $headers additional headers to send + * @param string $username Authentication username (deprecated) + * @param string $password Authentication password (deprecated) + * @return SoapClientResponse + * @throws \Exception if a cURL error occurs + */ + public static function send($url, $headers = array(), $body = null, $username = null, $password = null) + { + self::$handle = curl_init(); + + curl_setopt_array(self::$handle, array( + CURLOPT_URL => self::encodeUrl($url), + CURLOPT_POSTFIELDS => $body, + CURLOPT_HTTPHEADER => self::getFormattedHeaders($headers), + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_TIMEOUT => 10, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_SSL_VERIFYHOST => false, + CURLOPT_POST => true, + CURLOPT_VERBOSE => true, + CURLOPT_HEADER => true + )); + if (self::$socketTimeout !== null) { + curl_setopt(self::$handle, CURLOPT_TIMEOUT, self::$socketTimeout); + } + // supporting deprecated http auth method + if (!empty($username)) { + curl_setopt_array(self::$handle, array( + CURLOPT_HTTPAUTH => CURLAUTH_BASIC, + CURLOPT_USERPWD => $username . ':' . $password + )); + } + if (!empty(self::$auth['user'])) { + curl_setopt_array(self::$handle, array( + CURLOPT_HTTPAUTH => self::$auth['method'], + CURLOPT_USERPWD => self::$auth['user'] . ':' . self::$auth['pass'] + )); + } + if (self::$proxy['address'] !== false) { + curl_setopt_array(self::$handle, array( + CURLOPT_PROXYTYPE => self::$proxy['type'], + CURLOPT_PROXY => self::$proxy['address'], + CURLOPT_PROXYPORT => self::$proxy['port'], + CURLOPT_HTTPPROXYTUNNEL => self::$proxy['tunnel'], + CURLOPT_PROXYAUTH => self::$proxy['auth']['method'], + CURLOPT_PROXYUSERPWD => self::$proxy['auth']['user'] . ':' . self::$proxy['auth']['pass'] + )); + } + $response = curl_exec(self::$handle); + $error = curl_error(self::$handle); + $info = self::getInfo(); + if ($error) { + throw new \Exception($error); + } + // Split the full response in its headers and body + $header_size = $info['header_size']; + $header = substr($response, 0, $header_size); + $body = substr($response, $header_size); + + return new SoapClientResponse($info, $header, $body); + } + + public static function getInfo() + { + return curl_getinfo(self::$handle); + } + public static function getCurlHandle() + { + return self::$handle; + } + public static function getFormattedHeaders($headers) + { + $formattedHeaders = array(); + $combinedHeaders = array_change_key_case(array_merge((array) $headers, self::$defaultHeaders)); + foreach ($combinedHeaders as $key => $val) { + $formattedHeaders[] = $val; + } + if (!array_key_exists('user-agent', $combinedHeaders)) { + $formattedHeaders[] = 'user-agent: soapclient-request/1.0'; + } + if (!array_key_exists('expect', $combinedHeaders)) { + $formattedHeaders[] = 'expect:'; + } + return $formattedHeaders; + } + private static function getArrayFromQuerystring($query) + { + $query = preg_replace_callback('/(?:^|(?<=&))[^=[]+/', function ($match) { + return bin2hex(urldecode($match[0])); + }, $query); + parse_str($query, $values); + return array_combine(array_map('hex2bin', array_keys($values)), $values); + } + + /** + * Ensure that a URL is encoded and safe to use with cURL + * @param string $url URL to encode + * @return string + */ + private static function encodeUrl($url) + { + $url_parsed = parse_url($url); + $scheme = $url_parsed['scheme'] . '://'; + $host = $url_parsed['host']; + $port = (isset($url_parsed['port']) ? $url_parsed['port'] : null); + $path = (isset($url_parsed['path']) ? $url_parsed['path'] : null); + $query = (isset($url_parsed['query']) ? $url_parsed['query'] : null); + if ($query !== null) { + $query = '?' . http_build_query(self::getArrayFromQuerystring($query)); + } + if ($port && $port[0] !== ':') { + $port = ':' . $port; + } + $result = $scheme . $host . $port . $path . $query; + return $result; + } +} diff --git a/src/SoapClientCurl/SoapClientResponse.php b/src/SoapClientCurl/SoapClientResponse.php new file mode 100644 index 0000000..4da675a --- /dev/null +++ b/src/SoapClientCurl/SoapClientResponse.php @@ -0,0 +1,59 @@ +info = $info; + $this->headers = $this->parseHeaders($headers); + $this->body = $raw_body; + } + + /** + * if PECL_HTTP is not available use a fall back function + * + * thanks to ricardovermeltfoort@gmail.com + * http://php.net/manual/en/function.http-parse-headers.php#112986 + */ + private function parseHeaders($raw_headers) + { + if (function_exists('http_parse_headers')) { + return http_parse_headers($raw_headers); + } else { + $key = ''; + $headers = array(); + foreach (explode("\n", $raw_headers) as $i => $h) { + $h = explode(':', $h, 2); + if (isset($h[1])) { + if (!isset($headers[$h[0]])) { + $headers[$h[0]] = trim($h[1]); + } elseif (is_array($headers[$h[0]])) { + $headers[$h[0]] = array_merge($headers[$h[0]], array(trim($h[1]))); + } else { + $headers[$h[0]] = array_merge(array($headers[$h[0]]), array(trim($h[1]))); + } + $key = $h[0]; + } else { + if (substr($h[0], 0, 1) == "\t") { + $headers[$key] .= "\r\n\t" . trim($h[0]); + } elseif (!$key) { + $headers[0] = trim($h[0]); + } + } + } + return $headers; + } + } +} \ No newline at end of file diff --git a/tests/SoapClientRequestTest.php b/tests/SoapClientRequestTest.php new file mode 100644 index 0000000..d7626b0 --- /dev/null +++ b/tests/SoapClientRequestTest.php @@ -0,0 +1,29 @@ + + + + + + '.$claveAccesoComprobante.' + + + '; + +// Cabacera de la peticion +$headers = array('Content-Type: text/xml; charset=utf-8', 'Content-Length: '.strlen($body)); + +$result = SoapClientRequest::send($url,$headers, $body); + +var_dump($result); \ No newline at end of file