首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >PHP CURL有时返回空结果

PHP CURL有时返回空结果
EN

Stack Overflow用户
提问于 2018-02-16 15:05:08
回答 1查看 918关注 0票数 0

我在使用PHP的curl函数时遇到了一些麻烦。下面是对场景的简要描述,然后再转到代码。

它在命令行上运行的脚本,负责将来自web的信息导入本地数据库。

负责web查询的类执行三个步骤:

  1. 请求Azure oAuth令牌(此请求工作非常正常),该令牌不时被缓存和更新,不会出现重大问题。
  2. 执行一个API查询,返回包含某些对象的json。
  3. 第2项返回的数组被遍历并用于查询另一个API方法,该方法返回另一个json。

无论如何,我希望您能够清楚地了解这个场景,不幸的是,我无法传递关于这个API =/的太多细节。

现在,在代码中,我使用PHP函数访问API,正如我前面提到的,令牌工作得很好,最大的问题是在步骤2和步骤3中,有时步骤2的返回是空的,而另一些时候是从步骤3开始,在curl_error、curl_errno或curl_info中都不会产生错误(即使在这个过程中生成的HTTP代码是200)。

代码语言:javascript
运行
复制
<?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;
    }

}

由于我正在使用自己的缓存解决方案,所以我的类如下所示:

代码语言:javascript
运行
复制
<?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项不起作用,而第三次尝试则没有问题。LOL

提前感谢各位同事的时间和帮助。

EN

回答 1

Stack Overflow用户

发布于 2018-02-16 19:57:53

嗯,我想我可以自己解决这个问题,所以如果其他人也有同样的问题,我想留下我在这里找到的解决方案。

显然,问题在callService方法的这个代码块中:

代码语言:javascript
运行
复制
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生成的代码进行了比较,并更改了一些行,以适应邮递员正在使用的请求,修改后如下所示:

代码语言:javascript
运行
复制
    // 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,
        )
    ));

显然,对我来说,它解决了问题。谢谢!

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48829534

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档