Solicitud de caché remota (HTTP) con API de transitorios

8

Estoy tratando de usar el get_transient()método en mi Wordpress, he leído el documento y parece que estoy haciendo lo que se describe en los documentos.

Necesito mostrar el clima en mi sitio web y estoy usando una API meteorológica de terceros que se actualiza cada 6 horas.

Estamos creando un caché local del clima para que solo se llame a la API después del tiempo de caducidad. (Otro motivo: limitación de velocidad API)

Este es mi código:

$country   = 'India';
$API_Key  = 'xxxxxxxxxxxxxx';
$url        = 'http://weatherAPI.com/feed/weather.ashx?q='.$latlong.'&format=json&num_of_days=4&key='.$API_Key;

$weather = get_transient($location);
if (false === $weather) {
        $ch = curl_init();
        curl_setopt ($ch, CURLOPT_URL, $url);
        curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, 0);
        $weather = curl_exec($ch);
        curl_close($ch);
        set_transient($location, $weather, 60*60*6);
 }

Cuando envío una ubicación para obtener el clima ( say delhi) y si no está allí en el caché, esperaba que regresara falsemientras me devuelve la siguiente cadena

'{ "data": { "error": [ {"msg": "Unable to find any matching weather location to the query submitted!" } ] }}'

Solía var_dump($weather);comprobar el valor de$weather

¿Alguien puede corregirme donde estoy haciendo mal?

Umesh Awasthi
fuente
1
Esto no está relacionado con get_transient(), pero con la solicitud de la API: como se indica en el mensaje de error. Además de recomendar su uso wp_remote_post, solo tendrá que asegurarse de que la solicitud que se envía sea válida.
Stephen Harris
@StephenHarris: No estoy seguro acerca de la llamada, ya que solo se da dentro. if (false === $weather)He actualizado mi pregunta
Umesh Awasthi
1
El punto es que el caché almacenará lo que sea que le des, y en este caso estarás enviando un mensaje de error de la API meteorológica. El caché solo devolverá falso si no hay nada almacenado, por lo que debe verificar la respuesta de la API meteorológica y, si es válida, almacenarlo.
Stephen Harris
@StephenHarris: aha, entendí tu punto, lo perdí por completo. Acabo de comenzar con PHP y simplemente ignoré todo y ni siquiera miré cuidadosamente el código :)
Umesh Awasthi

Respuestas:

11

Capturando los datos remotos de la API meteorológica

El msgque estás mostrando en tu pregunta es básicamente el resultado de la API meteorológica. Y dice que no hay datos disponibles para su ubicación.

Lo primero que quiere hacer es investigar en el Codex y la "API WP HTTP" .

La forma correcta / WP de capturar datos remotos

Después de que haya aprendido sobre la API HTTP de WP, verá que la forma común de hacerlo es (simplificada de esta manera):

$response = wp_remote_request( 'http://example.com?some=parameter', array(
    'ssl_verify' => true
) );

Si hay un error (como se muestra en su ejemplo), podrá detectarlo utilizando la WP_Errorclase:

is_wp_error( $response ) AND printf(
    'There was an ERROR in your request.<br />Code: %s<br />Message: %s',
    $response->get_error_code(),
    $response->get_error_message()
);

Entonces es hora de obtener los datos apropiados. Esto se mostrará 200y OK, si todo en el lado remoto funcionó. IMPORTANTE: los datos remotos probablemente no seguirán un estándar que el interno. Por lo tanto, puede haber errores, pero aún recibirá el 200/OKmensaje positivo de ellos.

$response_code   = wp_remote_retrieve_response_code( $response );
$response_status = wp_remote_retrieve_response_message( $response );

Obtén el resultado

Finalmente es hora de inspeccionar el resultado. Primero, nos deshacemos de los espacios en blanco iniciales / finales. En el siguiente ejemplo, verá cómo usar la API HTTP de WP para verificar el encabezado. Si atrapamos JSON, entonces vamos con json_decode()y si tenemos XML, entonces vamos con la SimpleXMLclase nativa de PHP .

// Prepare the data:
$content = trim( wp_remote_retrieve_body( $response ) );
// Convert output to JSON
if ( strstr( wp_remote_retrieve_header( $response, 'content-type' ), 'json' ) )
{
    $content = json_decode( $content );
}
// … else, after a double check, we simply go with XML string
elseif ( strstr(
        wp_remote_retrieve_header( $response, 'content-type' ),
        'application/xhtml+xml'
    ) )
{
    // Lets make sure it is really an XML file
    // We also get cases where it's "<?XML" and "<?xml"
    if ( '<?xml' !== strtolower( substr( $content, 0, 5 ) ) )
        return false;

    // Also return stuff wrapped up in <![CDATA[Foo]]>
    $content = simplexml_load_string( $content, null, LIBXML_NOCDATA );
}
// If both didn't work out, then we maybe got a CSV, or something else...

En el caso de un archivo CSV, tendrá que encontrar una solución personalizada o buscar una clase PHP en los interwebs. Pero honestamente: si están usando CSV, es más fácil buscar otro servicio.

Caché los datos con un transitorio

La API transitoria ofrece una forma bastante agradable de hacer esto:

// Set Transient
$transient = set_transient(
    'Your cache key',
    $content,
    60*60*6
);

Entonces deberías poder atrapar al transitorio con get_transient().

Errores comunes

Un error frecuente es que la verificación SSL no funciona. Con mucho gusto puedes encenderlo / apagarlo bastante fácil:

// ON:
add_filter( 'https_ssl_verify', '__return_true' );
// OFF:
add_filter( 'https_ssl_verify', '__return_false' );

Hay una cosa bastante divertida, como descubrirá cuando inspeccione el archivo core apropiado: Core también tiene un filtro para solicitudes locales . Pero no te dejes engañar por este. ¡Este filtro solo está destinado a usarse en caso de que A) proporcione un servicio remoto desde su instalación WP y B) también lo consuma usted mismo! Lo sé, este puede ser un #WTF?!momento en el que este no es un cambio para que usted use diferentes configuraciones de verificación SSL entre su instalación local y su entorno / servidor de producción, pero también tiene una idea detrás: es probar los servicios que usted proporcione usted mismo como también le expliqué a la comunidad de WP G + aquí .

// Debug your own service without SSL verification.
add_filter( 'https_local_ssl_verify', '__return_false' );

Depuración de la solicitud y sus resultados

Sin profundizar demasiado en el proceso de actualización, pero la API WP HTTP utiliza la clase WP_HTTP. También ofrece algo agradable: un gancho de depuración.

do_action( 'http_api_debug', $response, 'response', $class, $args, $url );

Donde $responsetambién puede haber un WP_Errorobjeto que quizás te diga más.

Nota: a partir de una breve prueba, este filtro parece funcionar solo (por alguna razón) si lo coloca tan cerca de donde realmente está haciendo la solicitud. Entonces, tal vez deba llamarlo desde una devolución de llamada en uno de los siguientes filtros.

Y NO RIZO?

Fácil. Todo el funkiness de la "API HTTP WP", que he mostrado anteriormente, es básicamente un contenedor basado en funciones para las WP_HTTPpartes internas de la clase, que actúa como clase base (y se extenderá para diferentes escenarios). Los extienden WP_HTTP_*clases son Fsockopen, Streams, Curl, Proxy, Cookie, Encoding. Si 'http_api_debug'conecta una devolución de llamada a la acción, el tercer argumento le dirá qué clase se utilizó para su solicitud. No tiene que llamar a las clases directamente. Solo usa las funciones.

Para la mayoría de las solicitudes API remotas / HTTP, es la WP_HTTP_curlclase, que es un contenedor para la curlbiblioteca nativa de PHP .

Dentro de la WP_HTTP_curlclase, encontrarás el request()método. Este método ofrece dos filtros para interceptar el comportamiento SSL: uno para solicitudes locales 'https_local_ssl_verify'y otro para solicitudes remotas 'https_ssl_verify'. WP probablemente definirá localcomo localhosty lo que se obtiene returna partir get_option( 'siteurl' );.

emperador
fuente
Nota: Esta respuesta puede leerse como una extensión de esta respuesta que di .
Kaiser
Gracias por las entradas, no estoy seguro de la respuesta de la API de Waether ya que solo se llama cuando no tenemos datos, verifiqué la URL de la API y me devuelve datos válidos. Intentaré depurarla más
Umesh Awasthi
@UmeshAwasthi Intente lo que he escrito anteriormente, paso a paso. Primero vea de qué obtiene wp_remote_request()con su URL, luego vaya más allá con la respuesta. Es un tutorial bastante completo y le muestra la forma correcta de realizar solicitudes HTTP en WP. Para aclarar un poco más: no tiene que llamar a la WP_HTTP_curlclase, ya que WordPress ya lo hace por usted, cuando usa las funciones que se muestran arriba.
kaiser
estoy en camino de probar esto. Se actualizará una vez que lo complete :)
Umesh Awasthi
1
aunque el problema era otra cosa, pero marqué su respuesta como aceptada, ya que proporciona una mejor manera de hacer el mismo trabajo y soy de la opinión de que si la plataforma proporciona alguna API, es mejor usarla ¡Gracias!
Umesh Awasthi
3

El problema no es con la función 'transitorios'. Parece un mensaje de error devuelto por su API de terceros. Probablemente necesite verificar eso antes de usar set_transient. set_transientinsertará lo que se le haya dado y get_transientrecuperará lo que esté en la base de datos. En otras palabras, estoy bastante seguro de que el problema no está donde crees que está.

$weather = get_transient($location);
if (false === $weather) {
        $ch = curl_init();
        curl_setopt ($ch, CURLOPT_URL, $url);
        curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, 0);
        $weather = curl_exec($ch);
        curl_close($ch);
        // now check $weather to see if you got a valid result
        $check = json_decode($weather);
        if (isset($check->data->error)) {
          // there is a problem; do not insert; try something else
        } else {
          set_transient($location, $weather, 60*60*6);
        }
 }

Supongo que algunos de los resultados de su API meteorológica pueden ser necesarios para obtener los resultados que desea.

Nota: Su API está devolviendo JSON. Su ejemplo decodifica a:

stdClass::__set_state(array(
   'data' => 
  stdClass::__set_state(array(
     'error' => 
    array (
      0 => 
      stdClass::__set_state(array(
         'msg' => 'Unable to find any matching weather location to the query submitted!',
      )),
    ),
  )),
))
s_ha_dum
fuente
Gracias por el aporte, pero me parece bastante extraño, ya que solo la llamada a la API se da solo dentro de la if (false === $weather)declaración. No estaba al tanto de que la WP_HTTP_curlclase tratará de usar eso
Umesh Awasthi
@UmeshAwasthi, no hay nada extraño en ese condicional. Si tiene datos actuales en la memoria caché transitoria, no desea recuperarlos de la API. Solo si su caché transitoria está desactualizada, puede obtener nueva información.
s_ha_dum