Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Bom dia, pessoal!
Estou com um problema um tanto inconveniente utilizando as funções curl do PHP. Abaixo deixo uma breve descrição do cenário antes de passar ao código.
O Script é executado via linha de comando e é responsável pela importação para banco de dados local de informações vindas de um web api.
A classe responsável pela consulta a do web api executa três etapas:
-
Requisição de um token oAuth da Azure, esta requisição está funcionando perfeitamente, este token é armazenado em cache e renovado de tempos em tempos sem maiores problemas
-
É realizada uma consulta ao API que retorna um json contendo alguns objetos
-
O array retornado pelo item 2 é percorrido e utilizado para consultar outro método do API, que retorna outro json.
Enfim, espero que tenha conseguido ser claro no cenário, infelizmente não posso passar muitos detalhes sobre o API em questão =/
Agora o código, estou usando para o acesso ao API as funções de curl do PHP, como mencionei antes, o token funciona perfeitamente, o problema maior está nas etapas 2 e 3, algumas vezes o retorno da etapa 2 vem vazio, outras o da etapa 3, nunca é gerado erro nem no curl_error, nem no curl_errno e nem no curl_info (Incluso neste o código HTTP vem como 200).
<?php
namespace RestClient\Service;
/**
* Cliente abstrato de acesso ao webservice REST
* Este cliente é responsável por gerar o token para uso
* com os demais clientes.
*
* @author Rodrigo Teixeira Andreotti <ro.andriotti@gmail.com>
*/
abstract class AbstractClient
{
private $tokenUrl;
private $clientId;
private $secret;
private $serviceUrl;
private $resourceId;
private $tenantId;
private $apiKey;
private $cache;
/**
* Recebe em seu construtor uma instância da
* aplicação rodando e uma do handler de cache
*
* @param \Core\Application $app
* @param \Core\Cache\Cache $cache
*/
public function __construct(\Core\Application $app, \Core\Cache\Cache $cache)
{
$this->tokenUrl = $app->getConfig('api_token_url');
$this->clientId = $app->getConfig('api_clientId');
$this->secret = $app->getConfig('api_secret');
$this->serviceUrl = $app->getConfig('api_service_url');
$this->tenantId = $app->getConfig('api_tenantId');
$this->apiKey = $app->getConfig('api_key');
$this->resourceId = $app->getConfig('api_resourceId');
$this->cache = $cache;
$this->loadToken();
}
/**
* Verifica se existe um token válido em cache,
* caso haja o carrega, se não gera um novo token no webservice,
* o salva em cache e o retorna para uso pelo serviço.
*
* @uses AbstractClient::requestToken()
*
* @return string Token gerado / armazenado
*/
private function loadToken()
{
$cache = $this->cache;
$token = $cache->readCache('api_token');
if (!$token) {
$tokenData = $this->requestToken();
$cache->saveCache('api_token', $tokenData->access_token, 45); // <-- Converte o tempo do token para minutos
$token = $tokenData->access_token;
}
return $token;
}
/**
* Requisita ao webservice o token de acesso
*
* @return \stdClass Contém o json decodificado com as informações do token
*/
private function requestToken()
{
$ch = curl_init($this->tokenUrl . $this->tenantId . '/oauth2/token');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
'grant_type' => 'client_credentials',
'resource' => $this->resourceId,
'client_id' => $this->clientId,
'client_secret' => $this->secret
));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$data = json_decode(curl_exec($ch));
curl_close($ch);
return $data;
}
/**
* Realiza a consulta ao webserice
*
* @uses AbstractClient::buildUrlParams()
*
* @param string $method Método REST que será consultado
* @param array $params Paramentros adicionais que serão chamados
*
* @return \stdClass Retorno do json decodificado
*/
protected function callService($method, $params = null)
{
$ch = curl_init($this->serviceUrl . $method . ($params ? $this->buildUrlParams($params) : ''));
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, TRUE);
// Linhas abaixo necessárias para usar o cUrl com windows sem certificado
//curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
//curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt_array($ch, array(
CURLOPT_HTTPGET => TRUE,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer ' . $this->loadToken(),
'X-Api-Key: ' . $this->apiKey,
)
));
$response = curl_exec($ch);
$httpCode = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
if ($httpCode != 200) {
return $httpCode;
}
$data = json_decode($response);
curl_close($ch);
return $data;
}
/**
* Constrói os parâmetros em formato de URL
*
* @param array $params
* @return string Parametros de URL formatados
*/
private function buildUrlParams($params)
{
$urlParams = '';
if (count($params)) {
$urlParams .= '?';
$i = 0;
foreach ($params as $key => $param) {
$urlParams .= (($i == 0) ? '' : '&');
$urlParams .= $key . '=' . $param;
$i++;
}
}
return $urlParams;
}
}
Como eu estou utilizando uma solução própria para o cache, segue minha classe também:
<?php
namespace Core\Cache;
/**
* Sistema de cache
*
* @author Rodrigo Teixeira Andreotti <ro.andriotti@gmail.com>
*/
class Cache
{
/**
*
* @var integer Tempo para o cache em minutos
*/
private $time = 60;
/**
*
* @var string Local onde o cache será salvo
*/
private $local;
/**
* Inicializa a classe e define o local onde o cache será armazenado
* @uses Cache::setLocal()
* @param string $local
*/
public function __construct($local)
{
$this->setLocal($local);
}
/**
* Define o local onde o cache será salvo
* @param string $local
* @return $this
*/
private function setLocal($local)
{
if (!file_exists($local)){
trigger_error('Diretório de cache não encontrado', E_USER_ERROR);
} elseif(!is_dir($local)) {
trigger_error('Caminho para diretório de cache não aponta para um diretório', E_USER_ERROR);
} elseif(!is_writable($local)){
trigger_error('Diretório de cache inacessível', E_USER_ERROR);
} else {
$this->local = $local;
}
return $this;
}
/**
* Gera o local onde o arquivo será salvo
* @param string $key
* @return string
*/
private function generateFileLocation($key)
{
return $this->local . DIRECTORY_SEPARATOR . sha1($key) . '.tmp';
}
/**
*
* Cria o arquivo de cache
*
* @uses Cache::generateFileLocation()
*
* @param string $key
* @param mixed $content
*
* @return boolean
*/
private function generateCacheFile($key, $content)
{
$file = $this->generateFileLocation($key);
return file_put_contents($file, $content) || trigger_error('Não foi possível criar o arquivo de cache', E_USER_ERROR);
}
/**
*
* Salva um valor em cache
*
* @uses Cache::generateCacheFiles
*
* @param string $key
* @param mixed $content
* @param integer $time Tempo em minutos
*
* @return boolean
*/
public function saveCache($key, $content, $time = null)
{
$time = strtotime(($time ? $time : $this->time) . ' minutes');
$content = serialize(array(
'expira' => $time,
'content' => $content
));
return $this->generateCacheFile($key, $content);
}
/**
* Recupera um valor salvo no cache
*
* @uses Cache::generateFileLocation()
*
* @param string $key
*
* @return mixed Valor do cache salvo ou null
*/
public function readCache($key)
{
$file = $this->generateFileLocation($key);
if (is_file($file) && is_readable($file)) {
$cache = unserialize(file_get_contents($file));
if ($cache['expira'] > time()) {
return $cache['content'];
} else {
unlink($file);
}
}
return null;
}
}
Abaixo deixo também um print do var_dump do curl_info em uma das vezes que o problema ocorre.
Geralmente tem uma ordem específica para ocorrer o problema, na primeira tentativa o item 2 descrito acima não funciona, na segunda tentativa é o item 3 que não funciona e na terceira tentativa funciona tudo certo. kkk
[/applications/core/interface/imageproxy/imageproxy.php?img=https://i.stack.imgur.com/fxQ6G.png&key=3659b66a745c98522e5eea2d784337b624232a56982b7755639f379a2fb6e08d" />](https://i.stack.imgur.com/fxQ6G.png)
Desde já deixo meu agradecimento pelo tempo e ajuda dos colegas.Carregando comentários...