Mostofreddy

Clase para hacer peticiones http con cUrl y PHP (I)

Comentarios 1 Tags Categorias Programacion

{lang: 'es-419'}

cUrl es una librería que nos permite hacer peticiones con diferentes protocolos (http,file,ssl,etc), algo muy útil para la consulta de los tan en moda servicios Rest (tales como twitter, Google, etc).

Al utilizar cUrl debemos tener en cuenta dos cosas. La primera es como realizar la petición, para ello cree una clase llamada FlyCurl que contiene toda la lógica para realizarla y otra clase FlyRest que funciona como fachada de FlyCurl pero con los métodos necesarios para hacer peticiones Rest (POST, GET, DELETE, PUT). Lo segundo a tener en cuenta es como tratar el resultado de la petición, las respuestas cUrl no son del todo amigables y hay que trabajar con ellas, FlyCurlResponse devuelve la respuesta tal como fue obtenida pero FlyCurlResponseJson y FlyCUrlResponseXml las devuelve en los formatos Json y Xml respectivamente.  Utilice el patrón de diseño Strategy para definir el objeto que tratara la respuesta.

Les dejo un diagrama de clases para que entiendan la arquitectura

Arquitectura Fly Curl

FlyCurl.php

<?php
namespace Http;
/**
 * FlyCurl
 *
 * @package Fly
 * @subpackage Http
 * @author Federico Lozada Mosto <mostofreddy@gmail.com>
 * @copyright  2010 Federico Lozada Mosto <mostofreddy@gmail.com>
 * @license http://www.opensource.org/licenses/gpl-2.0.php  GNU General Public License (GPL)
 * @version 0.3
 * @link http://www.mostofreddy.com.ar
 */
class FlyCurl
{
 /**
 * Opciones Curl
 * @var array
 */
 protected $_curlOpts = array(
 CURLOPT_URL => '',
 CURLOPT_ENCODING => "", // soportar todos los "encodings"
 CURLOPT_CONNECTTIMEOUT => 120, // el timeout para realizar la coneccion
 CURLOPT_TIMEOUT => 120, // el timeout de respuesta de la web
 CURLOPT_AUTOREFERER => true, // set referer on redirect
 CURLOPT_RETURNTRANSFER => true, // devuelve la web
 CURLOPT_HEADER => true, // devuelve los headers de la web
 CURLOPT_FOLLOWLOCATION => true, // sigue redirecciones (por ej, si al iniciar curl es direccionado a otra web)
 CURLOPT_HTTPHEADER => array(),
 CURLOPT_MAXREDIRS => 5,  // maxima cantidad de redirecciones
 CURLOPT_USERAGENT => "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.15) Gecko/2008111317  Firefox/3.0.4", // el agente (datos sobre navegador)
 CURLOPT_COOKIEFILE => "", // de donde leer cookies
 CURLOPT_COOKIEJAR => "" // en donde guardar cookies
 );
 /**
 * Handler de conección
 * @var handler
 */
 protected $_conn = null;
 /**
 * Clase del objeto respuesta
 * @var string
 */
 protected $_classResponseDefault = 'Http\FlyCurlResponse';
 /**
 * Puntero a la ultima consulta realizada
 * @var unknown_type
 */
 protected $_oResponse = null;
 /**
 * Consutructor
 * @return Curl
 */
 public function __construct(){
 $this->_conn = $this->connect();
 }
 /**
 * Devuelve la version de curl instalada
 * @return array
 */
 public function curlVersion(){
 return curl_version();
 }
 /**
 * Setea la clase del objeto que se instanciara para la respuesta.
 * Por default es CurlResponse, si se define otra debe extender de esta
 * @param string $class
 * @return $this
 */
 public function setObjResponse(FlyCurlResponse $oResponse){
 $this->_oResponse = $oResponse;
 return $this;
 }
 /**
 * Setea la clase del objeto que se instanciara para la respuesta.
 * Por default es CurlResponse, si se define otra debe extender de esta
 * @param string $class
 * @return $this
 */
 public function setClassResponse($classResponse){
 $this->_classResponseDefault = $classResponse;
 return $this;
 }
 /**
 * Setea una opcion Curl
 * @final
 * @param string $k
 * @param mixed $v
 * @return $this
 */
 final public function setOpt($k,$v){
 $this->_curlOpts[$k] = $v;
 return $this;
 }
 /**
 * Recupera una configuracion Curl
 * @final
 * @param ustring $k
 * @return mixed
 */
 final public function getOpt($k){
 return $this->_curlOpts[$k]?:null;
 }
 /**
 * Establece una conexion Curl
 * @return handle
 */
 final protected function connect(){
 return curl_init();
 }
 /**
 * Cierra una conexion Curl
 * @return $this
 */
 public function close(){
 curl_close($this->_conn);
 return $this;
 }
 /**
 * Transforma los parametros en formato array a querystring
 * @param array $params
 * @return string
 */
 final public function buildQuery(array $params=array()){
 return is_array($params)?http_build_query($params):'';
 }
 /**
 * Realiza una peticion Curl
 * @param string $url url a la cual se realizara la peticion
 * @param string $query queryString
 * @return Rest\CurlResponse
 */
 public function exec($url){
 $this->setOpt(CURLOPT_URL,$url);
 curl_setopt_array($this->_conn, $this->_curlOpts);
 //si no esta creado el objeto response lo creo
 if($this->_oResponse === null)  $this->_oResponse = new $this->_classResponseDefault();
 //seteo la respuesta, la info de la respuesta y devuelve el objeto response
 return $this->_oResponse->setResponse(curl_exec($this->_conn))
 ->setResponseInfo(curl_getinfo($this->_conn))
 ->setError(curl_errno($this->_conn),curl_error($this->_conn))
 ->render();
 }
 /**
 * Devuelve la respuesta de la ultima consulta
 * @return object
 */
 public function getLastResponse(){
 return $this->_oResponse;
 }
}

FlyCurlResponse.php

<?php
namespace Http;
/**
 * FlyCurlResponse
 *
 * @package Fly
 * @subpackage Http
 * @author Federico Lozada Mosto <mostofreddy@gmail.com>
 * @copyright  2010 Federico Lozada Mosto <mostofreddy@gmail.com>
 * @license http://www.opensource.org/licenses/gpl-2.0.php  GNU General Public License (GPL)
 * @version 0.2
 * @link http://www.mostofreddy.com.ar
 */
class FlyCurlResponse
{
 /**
 * Data de la respuesta, contiene tanto el header como el body
 * @var string
 */
 protected $_response = null;
 /**
 * Informacion del estado de la respuesta
 * @var array
 */
 protected $_responseInfo = null;
 /**
 * Headers de la respuesta obtenida
 * @var string
 */
 protected $_headers = null;
 /**
 * Cuerpo de la respuesta obtenida
 * @var string
 */
 protected $_body = null;
 /**
 * Mensaje de error
 * @var string
 */
 protected $_error = '';
 /**
 * Estado de la respuesta
 * @var boolean
 */
 public $success = true;
 /**
 * Codigos de respuesta posibles en una petición
 * @var array
 */
 protected $_codes = Array(
 0   => 'buuuu',
 100 => 'Continue',
 101 => 'Switching Protocols',
 200 => 'OK',
 201 => 'Created',
 202 => 'Accepted',
 203 => 'Non-Authoritative Information',
 204 => 'No Content',
 205 => 'Reset Content',
 206 => 'Partial Content',
 300 => 'Multiple Choices',
 301 => 'Moved Permanently',
 302 => 'Found',
 303 => 'See Other',
 304 => 'Not Modified',
 305 => 'Use Proxy',
 306 => '(Unused)',
 307 => 'Temporary Redirect',
 400 => 'Bad Request',
 401 => 'Unauthorized',
 402 => 'Payment Required',
 403 => 'Forbidden',
 404 => 'Not Found',
 405 => 'Method Not Allowed',
 406 => 'Not Acceptable',
 407 => 'Proxy Authentication Required',
 408 => 'Request Timeout',
 409 => 'Conflict',
 410 => 'Gone',
 411 => 'Length Required',
 412 => 'Precondition Failed',
 413 => 'Request Entity Too Large',
 414 => 'Request-URI Too Long',
 415 => 'Unsupported Media Type',
 416 => 'Requested Range Not Satisfiable',
 417 => 'Expectation Failed',
 500 => 'Internal Server Error',
 501 => 'Not Implemented',
 502 => 'Bad Gateway',
 503 => 'Service Unavailable',
 504 => 'Gateway Timeout',
 505 => 'HTTP Version Not Supported'
 );
 /**
 * Constructor
 * @param string $response respuesta del la llamada curl
 * @return $this
 */
 public function __construct($response=null){
 $this->_response = $response;
 }
 /**
 * Setea una respuesta Curl al objeto, antes realiza una refresco del mismo
 * @param string $response respuesta del la llamada curl
 * @return $this
 */
 public function setResponse($response){
 $this->refresh();
 $this->_response = $response;
 return $this;
 }
 /**
 * Setea la info extra de una peticion curl
 * @param array $info info de una peticion
 * @return $this
 */
 final public function setResponseInfo(array $info){
 $this->_responseInfo = $info;
 return $this;
 }
 /**
 * Setea el error de una peticion curl
 * @param int $errno id del error
 * @param string $error string del error
 * @return $this
 */
 final public function setError($errno,$error){
 $this->_responseInfo['errno'] = $errno;
 $this->_responseInfo['error'] = $error;
 return $this;
 }
 /**
 * Renderiza la respuesta
 * @return $this
 */
 final public function render(){
 if($this->_responseInfo['errno']!==0){
 $this->success = false;
 $this->_error = $this->_responseInfo['error'];
 }elseif($this->_responseInfo['http_code']!==200){
 $this->success = false;
 $this->_error = $this->_codes[$this->_responseInfo['http_code']];
 }else{
 list($this->_headers, $this->_body) = explode("\r\n\r\n", $this->_response);
 //si curl no tiene habilitadas las cabeceras debo intercambiar los valores
 if($this->_body == ''){
 $this->_body = $this->_headers;
 $this->_headers = '';
 }
 }
 return $this;
 }
 /**
 * Devuelve los header de la respuesta curl si existieran
 * @return string
 */
 public function getHeaders(){
 return $this->_headers;
 }
 /**
 * Devuelve el cuerpo de la respuesta curl
 * @return string
 */
 public function getBody(){
 return $this->_body;
 }
 /**
 * Devuelve la respuesta curl, tanto los header como el cuerpo
 * @return string
 */
 public function getResponse(){
 return $this->_response;
 }
 /**
 * Devuelve la info de la respuesta curl
 * @return array
 */
 public function getResponseInfo(){
 return $this->_responseInfo;
 }
 /**
 * Devuelve el error
 * @return string
 */
 public function getError(){
 return $this->_error;
 }
 /**
 * Limpia el contenido del objeto y lo vuelve a su estado original
 * @return $this
 */
 public function refresh(){
 $this->_response = null;
 $this->_responseInfo = null;
 $this->_headers = null;
 $this->_body = null;
 $this->_error = '';
 $this->success = true;
 return $this;
 }
}

FlyCurlResponseJson.php

<?php
namespace Http;
/**
 * FlyCurlResponseJson
 *
 * @package Fly
 * @subpackage Http
 * @author Federico Lozada Mosto <mostofreddy@gmail.com>
 * @copyright  2010 Federico Lozada Mosto <mostofreddy@gmail.com>
 * @license http://www.opensource.org/licenses/gpl-2.0.php  GNU General Public License (GPL)
 * @version 0.1
 * @link http://www.mostofreddy.com.ar
 */
class FlyCurlResponseJson extends FlyCurlResponse
{
 /**
 * Devuelve el cuerpo de la respuesta curl
 * @return string
 */
 public function getBody(){
 return json_decode($this->_body);
 }
}

FlyCurlResponseXml.php

<?php
namespace Http;
/**
 * FlyCurlResponseXml
 *
 * @package Fly
 * @subpackage Http
 * @author Federico Lozada Mosto <mostofreddy@gmail.com>
 * @copyright  2010 Federico Lozada Mosto <mostofreddy@gmail.com>
 * @license http://www.opensource.org/licenses/gpl-2.0.php  GNU General Public License (GPL)
 * @version 0.1
 * @link http://www.mostofreddy.com.ar
 */
class FlyCurlResponseXml extends FlyCurlResponse
{
 /**
 * Devuelve el cuerpo de la respuesta curl
 * @return string
 */
 public function getBody(){
 return new \SimpleXMLElement($this->_body);
 }
}

Script de testeo

<?php
include '../Utils/autoload.php';
define('CLASS_DIR','../');
use Http as H;

$r = new H\FlyCurl();
$r->setClassResponse('Http\FlyCurlResponseXml');
$url = 'http://api.twitter.com/1/users/show/mostofreddy.xml';
//$url = 'http://www.bing.com';
$response = $r->exec($url);
?>

<pre>
<?php print_r($response->getHeaders());?>
</pre>

<br/><br/>++++++++++++++++++++++++++++++++++++++++<br/><br/>
<pre>
<?php

//print_r(json_decode($response->getBody()));
//echo $response->getBody();ç
//print_r($response->getBody());
//echo $response->getBody()->asXML();
highlight_string($response->getBody()->asXML())
?>
</pre>

En los próximos post veremos:

  • enviar archivos adjuntos
  • clase FlyRest
  • Multiples peticiones asincronicas con cUrl

0 comentarios - 1 pingbacks

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Enviar Comentario