¿Cómo establecer y usar variables globales? ¿O por qué no usarlos?

27

ACTUALIZACIÓN: Mi pregunta original se ha resuelto, pero esto se está convirtiendo en una discusión válida sobre por qué no usar variables globales, por lo que estoy actualizando la pregunta para reflejar eso. La solución fue <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?>como sugirió @TomJNowell.

ACTUALIZACIÓN 2: ahora tengo que hacer exactamente lo que quería. Pero todavía estoy usando el alcance global y estaría encantado de encontrar una mejor manera.

Estoy tratando de configurar un montón de variables globales para que los enlaces permanentes a categorías se utilicen en varios lugares de mi tema. La razón principal de esto es su uso tanto en la navegación principal como en una serie de subnavegaciones que se eligen en función de la categoría en la que se encuentra la publicación actual. Este no es un tema que lanzaré para que otros lo utilicen, pero está construido para un propósito muy específico.

Así es como los estoy creando actualmente (solo he pegado algunas de las variables).

function set_global_nav_var()
{
    //proposal
    global $prop;
    // Get the ID of a given category
    $category_id_prop = get_cat_ID( 'proposal' );
    // Get the URL of this category
    $category_link_prop = get_category_link( $category_id_prop );
    $prop = '<a href="' .esc_url( $category_link_prop ). '" title="Proposal">Proposal</a>';

    //Calvinball
    global $cb;
    // Get the ID of a given category
    $category_id_cb = get_cat_ID( 'calvinball' );
    // Get the URL of this category
    $category_link_cb = get_category_link( $category_id_cb );
    $cb = '<a href="' .esc_url( $category_link_cb). '" title="Calvinball">Calvinball</a>';
}
add_action( 'init', 'set_global_nav_var' );

Ahora puedo hacer en los <?php global $prop; echo $prop; ?>4 lugares que van y recuperar todo el enlace para el código. Cuando eso cambia, solo necesito cambiarlo en un lugar. Estoy abierto a alternativas que no involucran el alcance global.

JPollock
fuente
1
¿Qué enlace hace esta declaración echo esc_url ($ category_link_prop); muestra? ¿Cuál es tu enlace esperado?
Vinod Dalvi
1
¿Por qué no usarías simplemente 'get_cat_ID (****)' donde alguna vez planeaste usar la variable global? Dudo que haya alguna ventaja de velocidad en la forma en que lo haces. Desde el punto de vista de la legibilidad, 'get_cat_ID (****)' gana sin dudas.
Chris Strutton
1
¿Puedes reformular? Leí tu pregunta y todavía no estoy seguro de lo que quieres hacer y por qué quieres hacerlo. Mi consejo general sería no usar variables globales y no contaminar el alcance global
Tom J Nowell
1
este está sonando un poco como un X / Y Problema . quizás deberías retroceder y explicar exactamente cuál es el resultado deseado. Estoy seguro de que hay una solución mucho más elegante que configurar un montón de variables globales para luego simplemente codificar referencias a ellos en un navegador en otro lugar
Milo
2
cree una función que genere su menú en función del contexto que le pase, de esa manera puede mantener toda la lógica del menú y los valores asociados encapsulados en un solo lugar.
Milo

Respuestas:

21

Si bien desaconsejo esto, y no acelerará las cosas, su uso es incorrecto.

Cuando intente utilizar un global, primero debe especificar la palabra clave global. Lo ha especificado aquí al definir su valor, pero fuera de ese alcance, debe volver a declararse como una variable de alcance global.

Por ejemplo, en functions.php:

function test() {
    global $hello;
    $hello = 'hello world';
}
add_action( 'after_theme_setup', 'test' );

En single.php, esto no funcionará:

echo $hello;

Porque $ hello no está definido. Sin embargo, esto funcionará:

global $hello;
echo $hello;

Por supuesto que no debes hacer nada. WordPress ya intenta almacenar estas cosas en la memoria caché de objetos. No verá un aumento de velocidad al hacer esto (puede ver una pequeña disminución de la velocidad), todo lo que obtendrá es una complejidad adicional y la necesidad de escribir muchas declaraciones globales que no son necesarias.

Sería mejor usar datos estructurados, como objetos o inyección de dependencias, o en su caso, un conjunto de funciones.

Por ejemplo, aquí hay un medio para hacer algo similar a través de variables estáticas (aún malo por las mismas razones, pero solo un poco menos y más fácil de escribir), por ejemplo

function awful_function( $new_hello='' ) {
    static $hello;
    if ( !empty( $new_hello ) ) {
        $hello = $new_hello;
    }
    return $hello;
}

awful_function( 'telephone' );
echo awful_function(); // prints telephone
awful_function( 'banana');
echo awful_function(); // prints banana

Si realmente desea ahorrar tiempo almacenando datos en algún lugar para reutilizarlos, considere usar el WP_Cachesistema con wp_cache_getetc.

Tom J Nowell
fuente
Sé que es un poco loco usar el alcance global, pero la mayoría, si no todas estas variables se usarán en cada página. Estoy abierto a mejores ideas. Voy a editar la pregunta para que mi intención sea un poco más clara. Por cierto, funciona perfectamente bien cuando hago <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?>según su sugerencia. ¡Gracias!
JPollock
2
Ah, si mi solución funciona, ¿podría marcar como aceptada? Sus variables globales son tan rápidas como hacer la llamada original, es posible que desee probar el uso de funciones para no tener que escribir 2 líneas, mejor aún, un singleton, mejor aún, hacer toda esa dinámica y en un parte de la plantilla incluida a través de get_template_part
Tom J Nowell
Marcado como aceptado, es lo que estoy haciendo ahora, aunque puedo seguir con una de las estrategias que @MarkKaplun sugiere a continuación. Usar get_template_part () es una idea interesante, pero no estoy seguro de querer tener un directorio lleno de archivos cortos como ese ...
JPollock
oooh no no, no querrías un archivo para cada categoría, solo querrás el que toma el nombre de la categoría actual y lo usa. No debería tener que codificar nada, imagínese la molestia de codificarlo todo
Tom J Nowell
Puse el código en mi child-functions.php que está activo. Pero no puedo acceder a la variable en un archivo php-include que llamo desde una publicación "normal" generada por la base de datos. Avísame, ¿qué hago mal? (Lo defino como global, por supuesto).
ycc_swe
19

No use variables globales , tan simples como eso.

¿Por qué no usar globals?

Porque el uso de globales hace que sea más difícil mantener el software a largo plazo.

  • Se puede declarar un global en cualquier parte del código, o en ninguna parte, por lo tanto, no hay un lugar en el que pueda mirar instintivamente para encontrar algún comentario sobre para qué se utiliza el global.
  • Mientras lee el código, generalmente asume que las variables son locales para la función y no comprende que cambiar su valor en una función podría tener un cambio en todo el sistema.
  • Si no manejan la entrada, las funciones deben devolver el mismo valor / salida cuando se llaman con los mismos parámetros. El uso de globales en una función introduce parámetros adicionales que no están documentados en la declaración de la función.
  • los globales no tienen ninguna construcción de inicialización específica y, por lo tanto, nunca puede estar seguro de cuándo puede acceder al valor de la global, y no obtiene ningún error al intentar acceder a la global antes de la inicialización.
  • Alguien más (un complemento tal vez) podría usar globales con el mismo nombre, arruinando su código o arruinando su depende del orden de inicialización.

El núcleo de WordPress tiene mucho, mucho, mucho uso de los globales. Al intentar comprender cómo funcionan las funciones básicas como el the_contenttrabajo, de repente se da cuenta de que la $morevariable no es local sino global y necesita buscar en todos los archivos principales para comprender cuándo se establece en verdadero.

Entonces, ¿qué se puede hacer al intentar detener la copia y pegar varias líneas de código en lugar de almacenar el resultado de la primera ejecución en un global? Hay varios enfoques, funcionales y POO.

La función edulcorante. Es simplemente un contenedor / macro para guardar la copia / pegar

// input: $id - the category id
// returns: the foo2 value of the category
function notaglobal($id) {
  $a = foo1($id);
  $b = foo2($a);
  return $b;
}

Los beneficios son que ahora hay una documentación de lo que hace el antiguo global, y tiene un punto obvio para la depuración cuando el valor que se devuelve no es el que esperaba.

Una vez que tenga un edulcorante, es fácil almacenar en caché el resultado si es necesario (hágalo solo si descubre que esta función tarda mucho tiempo en ejecutarse)

function notaglobal($id) {
  static $cache;

  if (!isset($cache)) {
    $a = foo1($id);
    $b = foo2($a);
    $cache = $b;
  } 
  return $cache;
} 

Esto le brinda el mismo comportamiento de un global pero con la ventaja de tener una inicialización segura cada vez que accede a él.

Puede tener patrones similares con OOP. Encuentro que OOP generalmente no agrega ningún valor en complementos y temas, pero esta es una discusión diferente

class notaglobal {
   var latestfoo2;

   __constructor($id) {
     $a = foo1($id);
     $this->latestfoo2 = foo2($a)
   }
}

$v = new notaglobal($cat_id);
echo $v->latestfoo2;

Este es un código más torpe, pero si tiene varios valores que le gustaría calcular previamente porque siempre se usan, puede ser un camino a seguir. Básicamente, este es un objeto que contiene todos sus globales de manera organizada. Para evitar que una instancia de este objeto sea global (desea una sola instancia; de lo contrario, vuelva a calcular los valores), es posible que desee utilizar un patrón singleton (algunas personas argumentan que es una mala idea, YMMV)

No me gusta acceder a un atributo de objeto directamente, por lo que en mi código se deformará un poco más

class notaglobal {
   var latestfoo2;

   __constructor() {}

   foo2($id) {  
     if (!isset($this->latestfoo2)) {    
       $a = foo1($id);
       $b = foo2($a);
       $this->latestfoo2= $b;
     } 
     return $this->latestfoo2;
   }
}

$v = new notaglobal();
echo $v->foo2($cat_id);
Mark Kaplun
fuente
77
Por favor, no grites . ¿Te importaría explicar por qué y proporcionar algún tipo de cita?
brasofilo
Creo que entendiste mal la respuesta. Si no intentara hacer una optimización temprana almacenando valores en variables globales, su código habría funcionado. Los gritos se deben a que seguir los principios básicos de desarrollo de software establecidos es algo que no se puede enfatizar lo suficiente. Las personas que no entienden esos principios básicos (disponibles en su google local) no deben difundir el código en la red.
Mark Kaplun
1
En mi opinión, esta es una respuesta, las personas que vienen aquí desde Google deberían ver que es una mala idea incluso pensar en usar globals de inmediato.
Mark Kaplun
66
No es suficiente decir que no hagas X, tienes que explicar por qué o parece que lo dices por capricho
Tom J Nowell
1
@ TomJNowell, me parece curioso que yo fuera el único que rechazó la pregunta en sí, ya que obviamente estaba fuera del alcance de WASE. No vi el valor de expandir un tema que no debería haber comenzado aquí en absoluto.
Mark Kaplun
8

Su pregunta está relacionada con el funcionamiento de php.

Tome $ wpdb como ejemplo

$ wpdb es una variable global bien conocida.

¿Sabes cuándo será declarado y asignado con valores?

Cada página cargada , sí, cada vez que visita su sitio de WordPress.

Del mismo modo, debe asegurarse de que las variables que desea globalizar se declaren y asignen con los valores correspondientes en cada página cargada.

Aunque no soy un diseñador de temas, puedo decir que after_setup_theme es un gancho único. solo se activará cuando se active el tema.

Si fuera usted, usaría init u otros ganchos. No, si fuera usted, no usaré variables globales en absoluto ...

Realmente no soy bueno para explicar las cosas. Por lo tanto, debe elegir un libro si desea profundizar en PHP.

Jesse
fuente
2

Siempre puede usar un patrón singleton a través de captadores estáticos.

<ul>
    <li><?php echo MyGlobals::get_nav_prop( 'proposal' )[ 'html' ]; ?></li>
    <li><?php echo MyGlobals::get_nav_prop( 'calvinball', 'html' ); ?></li>
</ul>


<?php

if ( ! class_exists('MyGlobals') ):

class MyGlobals {

    public $props;

    public function __construct(){
      $this->props = array (
        'proposal' => array( 'title' => 'Proposal', 'text' => 'Proposal' ),
        'calvinball' => array( 'title' => 'Calvinball', 'text' => 'Calvinball' ),
      );
    }

    public function get_nav_prop ( $term, $prop = false )
    {
      $o = self::instance();
      if ( ! isset( $o->props[$term] ) ) {  return falst; }
      if ( ! isset( $o->props[$term][ 'html' ] ) ) {
          $id = get_cat_ID( $term );
          $link = esc_url ( get_category_link( $id ) );
          $title = $o->props[$term]['title'];
          $text = $o->props[$term]['text'];
          $o->props[$term]['html'] = '<a href="'.$link.'" title="'.$title.'">'.$text.'</a>';
          $o->props[$term]['link'] = $link;
          $o->props[$term]['id'] = $id;
      }

      if($prop){ return isset($o->props[$term][$prop]) ? $o->props[$term][$prop] : null; }

      return $o->props[$term];
    }

    // -------------------------------------

    private static $_instance;

    public static function instance(){

      if(!isset(self::$_instance)) {
        self::$_instance = new MyGlobals();
      }
      return self::$_instance;
    }

}

endif; // end MyGlobals
jgraup
fuente