使用PHP的curl函数我有一点不方便的问题。下面是在继续编写代码之前对场景的简要描述。
它在命令行上运行的脚本,负责将来自web api的信息导入本地数据库。
负责web api查询的类执行三个步骤:
无论如何,我希望你能够清楚地了解这个场景,遗憾的是我无法传递关于这个API的太多细节= /
现在,代码,我正在使用PHP curl函数来访问API,正如我之前提到的,令牌工作正常,最大的问题在于步骤2和3,有时从步骤2返回是空的,其他从第3步开始,在curl_error,curl_errno或curl_info中都没有生成错误(即使在这一个中,生成的HTTP代码也是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;
}
}
由于我使用自己的缓存解决方案,我的课程如下:
<?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;
}
}
一般有一个特定的顺序发生问题,在上面描述的第一次尝试项目2不起作用,在第二次尝试是项目3不起作用和第三次尝试工作正常。洛尔
提前感谢您的同事们的时间和帮助。
答案 0 :(得分:0)
嗯,我想我可以自己解决这个问题,所以如果其他人遇到同样的问题,我想留下我在这里的解决方案。
显然问题出在callService方法的代码块中:
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,
)
));
我无法找出确切的原因,但我将它与Postman生成的代码进行了比较,并更改了一些行以适应邮递员正在使用的请求,在更改后看起来像这样:
// 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);
$token = $this->loadToken();
curl_setopt_array($ch, array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPGET => TRUE,
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer ' . $token,
'Cache-Control: no-cache',
'X-Api-Key: ' . $this->apiKey,
)
));
对我来说,显然,它解决了这个问题。谢谢!