Skip to content

Commit bf3e43c

Browse files
author
Alejandro Labrada Diaz
committed
First Commit
0 parents  commit bf3e43c

File tree

8 files changed

+304
-0
lines changed

8 files changed

+304
-0
lines changed

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
### Composer template
2+
composer.phar
3+
vendor/
4+
composer.lock
5+
.idea
6+
7+
# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
8+
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
9+
# composer.lock

LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2015 Alejandro Labrada Diaz
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Soap Client using Curl [![version][packagist-version]][packagist-url]
2+
3+
[![Downloads][packagist-downloads]][packagist-url]
4+
[![Dependencies][versioneye-image]][versioneye-url]
5+
[![License][packagist-license]][license-url]

composer.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "aleplusplus/soapclient-curl-php",
3+
"description": "Soap Client using Curl",
4+
"keywords": ["soap", "curl", "non-wsdl", "client", "sri"],
5+
"type": "library",
6+
"require": {
7+
"php": ">=5.4.0",
8+
"ext-curl": "*",
9+
"ext-json": "*"
10+
},
11+
"license": "MIT",
12+
"authors": [
13+
{
14+
"name": "Alejandro Labrada Diaz",
15+
"email": "aleplusplus@gmail.com"
16+
}
17+
],
18+
"minimum-stability": "dev",
19+
"autoload": {
20+
"psr-0": {
21+
"SoapClientCurl": "src"
22+
}
23+
},
24+
"support": {
25+
"email": "aleplusplus@gmail.com"
26+
}
27+
}

src/SoapClientCurl.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
3+
require_once dirname(__FILE__) . '/SoapClientCurl/SoapClientRequest.php';
4+
require_once dirname(__FILE__) . '/SoapClientCurl/SoapClientResponse.php';
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php
2+
3+
namespace SoapClientCurl;
4+
5+
class SoapClientRequest {
6+
7+
private static $handle = null;
8+
private static $socketTimeout = null;
9+
private static $defaultHeaders = array();
10+
11+
private static $auth = array (
12+
'user' => '',
13+
'pass' => '',
14+
'method' => CURLAUTH_BASIC
15+
);
16+
private static $proxy = array(
17+
'port' => false,
18+
'tunnel' => false,
19+
'address' => false,
20+
'type' => CURLPROXY_HTTP,
21+
'auth' => array (
22+
'user' => '',
23+
'pass' => '',
24+
'method' => CURLAUTH_BASIC
25+
)
26+
);
27+
28+
/**
29+
* Send a cURL request
30+
* @param string $url URL to send the request to
31+
* @param mixed $body request body
32+
* @param array $headers additional headers to send
33+
* @param string $username Authentication username (deprecated)
34+
* @param string $password Authentication password (deprecated)
35+
* @return SoapClientResponse
36+
* @throws \Exception if a cURL error occurs
37+
*/
38+
public static function send($url, $headers = array(), $body = null, $username = null, $password = null)
39+
{
40+
self::$handle = curl_init();
41+
42+
curl_setopt_array(self::$handle, array(
43+
CURLOPT_URL => self::encodeUrl($url),
44+
CURLOPT_POSTFIELDS => $body,
45+
CURLOPT_HTTPHEADER => self::getFormattedHeaders($headers),
46+
CURLOPT_CONNECTTIMEOUT => 10,
47+
CURLOPT_TIMEOUT => 10,
48+
CURLOPT_RETURNTRANSFER => true,
49+
CURLOPT_SSL_VERIFYPEER => false,
50+
CURLOPT_SSL_VERIFYHOST => false,
51+
CURLOPT_POST => true,
52+
CURLOPT_VERBOSE => true,
53+
CURLOPT_HEADER => true
54+
));
55+
if (self::$socketTimeout !== null) {
56+
curl_setopt(self::$handle, CURLOPT_TIMEOUT, self::$socketTimeout);
57+
}
58+
// supporting deprecated http auth method
59+
if (!empty($username)) {
60+
curl_setopt_array(self::$handle, array(
61+
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
62+
CURLOPT_USERPWD => $username . ':' . $password
63+
));
64+
}
65+
if (!empty(self::$auth['user'])) {
66+
curl_setopt_array(self::$handle, array(
67+
CURLOPT_HTTPAUTH => self::$auth['method'],
68+
CURLOPT_USERPWD => self::$auth['user'] . ':' . self::$auth['pass']
69+
));
70+
}
71+
if (self::$proxy['address'] !== false) {
72+
curl_setopt_array(self::$handle, array(
73+
CURLOPT_PROXYTYPE => self::$proxy['type'],
74+
CURLOPT_PROXY => self::$proxy['address'],
75+
CURLOPT_PROXYPORT => self::$proxy['port'],
76+
CURLOPT_HTTPPROXYTUNNEL => self::$proxy['tunnel'],
77+
CURLOPT_PROXYAUTH => self::$proxy['auth']['method'],
78+
CURLOPT_PROXYUSERPWD => self::$proxy['auth']['user'] . ':' . self::$proxy['auth']['pass']
79+
));
80+
}
81+
$response = curl_exec(self::$handle);
82+
$error = curl_error(self::$handle);
83+
$info = self::getInfo();
84+
if ($error) {
85+
throw new \Exception($error);
86+
}
87+
// Split the full response in its headers and body
88+
$header_size = $info['header_size'];
89+
$header = substr($response, 0, $header_size);
90+
$body = substr($response, $header_size);
91+
92+
return new SoapClientResponse($info, $header, $body);
93+
}
94+
95+
public static function getInfo()
96+
{
97+
return curl_getinfo(self::$handle);
98+
}
99+
public static function getCurlHandle()
100+
{
101+
return self::$handle;
102+
}
103+
public static function getFormattedHeaders($headers)
104+
{
105+
$formattedHeaders = array();
106+
$combinedHeaders = array_change_key_case(array_merge((array) $headers, self::$defaultHeaders));
107+
foreach ($combinedHeaders as $key => $val) {
108+
$formattedHeaders[] = $val;
109+
}
110+
if (!array_key_exists('user-agent', $combinedHeaders)) {
111+
$formattedHeaders[] = 'user-agent: soapclient-request/1.0';
112+
}
113+
if (!array_key_exists('expect', $combinedHeaders)) {
114+
$formattedHeaders[] = 'expect:';
115+
}
116+
return $formattedHeaders;
117+
}
118+
private static function getArrayFromQuerystring($query)
119+
{
120+
$query = preg_replace_callback('/(?:^|(?<=&))[^=[]+/', function ($match) {
121+
return bin2hex(urldecode($match[0]));
122+
}, $query);
123+
parse_str($query, $values);
124+
return array_combine(array_map('hex2bin', array_keys($values)), $values);
125+
}
126+
127+
/**
128+
* Ensure that a URL is encoded and safe to use with cURL
129+
* @param string $url URL to encode
130+
* @return string
131+
*/
132+
private static function encodeUrl($url)
133+
{
134+
$url_parsed = parse_url($url);
135+
$scheme = $url_parsed['scheme'] . '://';
136+
$host = $url_parsed['host'];
137+
$port = (isset($url_parsed['port']) ? $url_parsed['port'] : null);
138+
$path = (isset($url_parsed['path']) ? $url_parsed['path'] : null);
139+
$query = (isset($url_parsed['query']) ? $url_parsed['query'] : null);
140+
if ($query !== null) {
141+
$query = '?' . http_build_query(self::getArrayFromQuerystring($query));
142+
}
143+
if ($port && $port[0] !== ':') {
144+
$port = ':' . $port;
145+
}
146+
$result = $scheme . $host . $port . $path . $query;
147+
return $result;
148+
}
149+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace SoapClientCurl;
4+
5+
class SoapClientResponse
6+
{
7+
public $info;
8+
public $headers;
9+
public $body;
10+
11+
/**
12+
* @param array $info response curl_getinfo in Curl request
13+
* @param string $headers raw header string from cURL response
14+
* @param string $raw_body the raw body of the cURL response
15+
* @internal param array $json_args arguments to pass to json_decode function
16+
*/
17+
public function __construct($info, $headers, $raw_body)
18+
{
19+
$this->info = $info;
20+
$this->headers = $this->parseHeaders($headers);
21+
$this->body = $raw_body;
22+
}
23+
24+
/**
25+
* if PECL_HTTP is not available use a fall back function
26+
*
27+
* thanks to ricardovermeltfoort@gmail.com
28+
* http://php.net/manual/en/function.http-parse-headers.php#112986
29+
*/
30+
private function parseHeaders($raw_headers)
31+
{
32+
if (function_exists('http_parse_headers')) {
33+
return http_parse_headers($raw_headers);
34+
} else {
35+
$key = '';
36+
$headers = array();
37+
foreach (explode("\n", $raw_headers) as $i => $h) {
38+
$h = explode(':', $h, 2);
39+
if (isset($h[1])) {
40+
if (!isset($headers[$h[0]])) {
41+
$headers[$h[0]] = trim($h[1]);
42+
} elseif (is_array($headers[$h[0]])) {
43+
$headers[$h[0]] = array_merge($headers[$h[0]], array(trim($h[1])));
44+
} else {
45+
$headers[$h[0]] = array_merge(array($headers[$h[0]]), array(trim($h[1])));
46+
}
47+
$key = $h[0];
48+
} else {
49+
if (substr($h[0], 0, 1) == "\t") {
50+
$headers[$key] .= "\r\n\t" . trim($h[0]);
51+
} elseif (!$key) {
52+
$headers[0] = trim($h[0]);
53+
}
54+
}
55+
}
56+
return $headers;
57+
}
58+
}
59+
}

tests/SoapClientRequestTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
require_once __DIR__ . '/../vendor/autoload.php'; // Autoload files using Composer autoload
4+
5+
use SoapClientCurl\SoapClientRequest;
6+
7+
// Clave de Acceso para una factura de Esdinamico en Pruebas
8+
$claveAccesoComprobante = '1302201501179188978900110010010000100520001005215';
9+
10+
// Ambiente de prueba de SRI
11+
$url = 'https://celcer.sri.gob.ec/comprobantes-electronicos-ws/AutorizacionComprobantes';
12+
13+
// Cuerpo del SOAP para la consulta
14+
$body = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ec="http://ec.gob.sri.ws.autorizacion">
15+
<soapenv:Header/>
16+
<soapenv:Body>
17+
<ec:autorizacionComprobante>
18+
<!--Optional:-->
19+
<claveAccesoComprobante>'.$claveAccesoComprobante.'</claveAccesoComprobante>
20+
</ec:autorizacionComprobante>
21+
</soapenv:Body>
22+
</soapenv:Envelope>';
23+
24+
// Cabacera de la peticion
25+
$headers = array('Content-Type: text/xml; charset=utf-8', 'Content-Length: '.strlen($body));
26+
27+
$result = SoapClientRequest::send($url,$headers, $body);
28+
29+
var_dump($result);

0 commit comments

Comments
 (0)