Comment définir et utiliser des variables globales? Ou pourquoi ne pas les utiliser du tout

27

MISE À JOUR: Ma question d'origine a été résolue, mais cela se transforme en une discussion valable sur pourquoi ne pas utiliser des variables globales, donc je mets à jour la question pour refléter cela. La solution était <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?>comme l'a suggéré @TomJNowell.

MISE À JOUR 2: Je l'ai maintenant en faisant exactement ce que je voulais. Mais j'utilise toujours la portée mondiale et serais heureux de trouver une meilleure façon.

J'essaie de mettre en place tout un tas de variables globales pour les permaliens vers des catégories à utiliser à divers endroits de mon thème. La principale raison en est à utiliser à la fois dans la navigation principale, ainsi que dans une série de sous-navigations qui sont choisies en fonction de la catégorie dans laquelle le message actuel se trouve. Ce n'est pas un thème que je publierai pour une utilisation par d'autres, mais est construit dans un but très spécifique.

C'est ainsi que je les crée actuellement (je n'ai collé que quelques-unes des 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' );

Je peux maintenant faire dans les <?php global $prop; echo $prop; ?>4 endroits qui vont récupérer le lien complet pour le code. Lorsque cela change, je n'ai qu'à le changer en un seul endroit. Je suis ouvert à des alternatives qui n'impliquent pas la portée mondiale.

JPollock
la source
1
Quel lien fait cette déclaration echo esc_url ($ category_link_prop); affiche? Quel est votre lien attendu?
Vinod Dalvi
1
Pourquoi n'utilisez-vous pas simplement 'get_cat_ID (****)' là où vous avez prévu d'utiliser la variable globale. Je doute qu'il y ait un avantage de vitesse sur la façon dont vous le faites. Du point de vue de la lisibilité, «get_cat_ID (****)» gagne haut la main.
Chris Strutton
1
Pouvez-vous reformuler? J'ai lu votre question et je ne sais toujours pas ce que vous voulez faire et pourquoi vous voulez le faire. Mon conseil général serait de ne pas utiliser de variables globales et de ne pas polluer la portée globale
Tom J Nowell
1
cela sonne un peu comme un X / Y problème . vous devriez peut-être sauvegarder et expliquer exactement quel est le résultat souhaité. Je suis certain qu'il existe une solution beaucoup plus élégante que de définir un tas de vars globaux pour ensuite simplement les coder en dur dans une navigation ailleurs
Milo
2
créez une fonction qui génère votre menu en fonction du contexte que vous lui passez, de cette façon vous pouvez conserver toute la logique du menu et les variables associées encapsulées en un seul endroit.
Milo

Réponses:

21

Bien que je déconseille fortement cela et que cela n'accélère pas les choses, votre utilisation est incorrecte.

Lorsque vous essayez d'utiliser un global, vous devez d'abord spécifier le mot-clé global. Vous l'avez spécifié ici lors de la définition de sa valeur, mais en dehors de cette portée, il doit être redéclaré en tant que variable de portée globale.

par exemple dans functions.php:

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

Dans single.php, cela ne fonctionnera pas:

echo $hello;

Parce que $ hello n'est pas défini. Cependant , cela va fonctionner:

global $hello;
echo $hello;

Bien sûr, vous ne devriez faire ni l'un ni l'autre. WordPress tente déjà de mettre ces choses en cache dans le cache d'objets. Vous ne verrez aucune augmentation de vitesse en faisant cela (vous pouvez voir une petite diminution de vitesse), tout ce que vous obtiendrez sera une complexité supplémentaire et la nécessité de taper un grand nombre de déclarations globales qui ne sont pas nécessaires.

Vous feriez mieux d'utiliser des données structurées, telles que des objets ou l'injection de dépendances, ou dans votre cas, un ensemble de fonctions.

Par exemple, voici un moyen de faire quelque chose de similaire via des variables statiques (toujours mauvaises pour les mêmes raisons, mais juste un tout petit peu moins et plus faciles à taper), par exemple

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 vous voulez vraiment gagner du temps en stockant les données quelque part pour les réutiliser, pensez à utiliser le WP_Cachesystème avec wp_cache_getetc

Tom J Nowell
la source
Je sais que c'est un peu fou d'utiliser la portée globale, mais la plupart, sinon toutes ces variables seront utilisées sur chaque page. Je suis ouvert à de meilleures idées. Je vais modifier la question pour clarifier mon intention. BTW cela fonctionne parfaitement bien quand je fais <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?>selon votre suggestion. Merci!
JPollock
2
Ah si ma solution fonctionne, pourriez-vous marquer comme acceptée? Vos variables globales sont aussi rapides que l'appel initial, vous pouvez essayer d'utiliser des fonctions à la place afin de ne pas avoir besoin de taper 2 lignes, mieux encore, un singleton, mieux encore, rendre tout cela dynamique et dans un partie de modèle incluse via get_template_part
Tom J Nowell
Marqué comme accepté comme ce que je fais maintenant, bien que je puisse aller avec l'une des stratégies proposées par @MarkKaplun ci-dessous. Utiliser get_template_part () est une idée intéressante, mais je ne suis pas sûr de vouloir avoir un répertoire plein de fichiers courts comme ça ...
JPollock
oooh non non vous ne voudriez pas un fichier pour chaque catégorie, vous voudriez juste celui qui saisit le nom de la catégorie actuelle et l'utilise. Vous ne devriez rien avoir à coder en dur, imaginez les tracas de tout coder en dur
Tom J Nowell
J'ai mis le code dans mon child-functions.php qui est actif. Mais je ne peux pas accéder à la variable dans un fichier php-include que j'appelle à partir d'une publication générée par une base de données "normale". Veuillez me conseiller, que dois-je faire de mal? (Je le définis comme global, bien sûr.)
ycc_swe
19

N'utilisez pas de variables globales , aussi simple que cela.

Pourquoi ne pas utiliser les globaux

Parce que l'utilisation de globaux rend plus difficile la maintenance du logiciel à long terme.

  • Un global peut être déclaré n'importe où dans le code, ou nulle part du tout, donc il n'y a aucun endroit où vous pouvez instinctivement regarder pour trouver des commentaires sur l'utilisation du global
  • Lors de la lecture du code, vous supposez généralement que les variables sont locales à la fonction et ne comprenez pas que la modification de leur valeur dans une fonction peut avoir un changement à l'échelle du système.
  • S'ils ne gèrent pas d'entrée, les fonctions doivent retourner la même valeur / sortie lorsqu'elles sont appelées avec les mêmes paramètres. L'utilisation de globaux dans une fonction introduit des paramètres supplémentaires qui ne sont pas documentés dans la déclaration de fonction.
  • les globaux n'ont pas de construction d'initialisation spécifique et vous ne pouvez donc jamais être sûr quand vous pouvez accéder à la valeur du global, et vous ne recevez aucune erreur lorsque vous essayez d'accéder au global avant l'initialisation.
  • Quelqu'un d'autre (un plugin peut-être) pourrait utiliser des globales avec le même nom, ruinant votre code, ou vous ruinant son selon l'ordre d'initialisation.

Le noyau WordPress a beaucoup plus que beaucoup recours aux globaux. Tout en essayant de comprendre comment fonctionnent les fonctions de base comme le the_contenttravail, vous réalisez soudain que la $morevariable n'est pas locale mais globale et que vous devez rechercher l'ensemble des fichiers de base pour comprendre quand elle est définie sur true.

Alors, que peut-on faire en essayant d'arrêter de copier et coller plusieurs lignes de code au lieu de stocker le résultat de la première exécution dans un global? Il existe plusieurs approches, fonctionnelles et POO.

La fonction édulcorante. Il s'agit simplement d'un wrapper / macro pour enregistrer le copier / coller

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

Les avantages sont que maintenant il existe une documentation sur ce que fait l'ancien global, et vous avez un point évident pour le débogage lorsque la valeur renvoyée n'est pas celle que vous attendez.

Une fois que vous avez un édulcorant, il est facile de mettre en cache le résultat si nécessaire (ne le faites que si vous découvrez que cette fonction prend beaucoup de temps à exécuter)

function notaglobal($id) {
  static $cache;

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

Cela vous donne le même comportement d'un global mais avec l'avantage d'avoir une initialisation assurée à chaque fois que vous y accédez.

Vous pouvez avoir des modèles similaires avec la POO. Je trouve que la POO n'ajoute généralement aucune valeur dans les plugins et les thèmes, mais c'est une discussion différente

class notaglobal {
   var latestfoo2;

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

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

Il s'agit d'un code plus maladroit, mais si vous avez plusieurs valeurs que vous souhaitez précalculer car elles sont toujours utilisées, cela peut être une solution. Fondamentalement, c'est un objet qui contient tous vos globaux de manière organisée. Pour éviter de faire d'une instance de cet objet un global (vous voulez avoir une instance sinon vous recalculez les valeurs) vous pouvez utiliser un modèle singleton (certaines personnes soutiennent que c'est une mauvaise idée, YMMV)

Je n'aime pas accéder directement à un attribut d'objet, donc dans mon code cela déformera un peu plus

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
la source
7
S'il vous plaît, ne criez pas . Pensez à expliquer pourquoi et à fournir une sorte de citation?
brasofilo
Je pense que vous avez mal compris la réponse. S'il n'essayait pas de faire une optimisation précoce en stockant des valeurs dans des variables globales, son code aurait fonctionné. Ce cri est dû au fait que suivre les principes de base du développement logiciel est quelque chose qui ne peut pas être suffisamment souligné. Les personnes qui ne comprennent pas ces principes de base (disponibles sur votre google local) ne devraient pas diffuser de code sur le net.
Mark Kaplun
1
OMI c'est une réponse, les gens qui viennent ici de google devraient voir que c'est une mauvaise idée de penser même à utiliser des globaux tout de suite.
Mark Kaplun
6
Il ne suffit pas de dire ne faites pas X, vous devez expliquer pourquoi ou vous semblez le dire sur un coup de tête
Tom J Nowell
1
@TomJNowell, je trouve drôle que j'étais le seul à voter contre la question elle-même, car elle était manifestement en dehors du champ d'application de WASE. Je ne voyais pas l'intérêt d'étendre un sujet qui n'aurait pas du tout dû être commencé ici.
Mark Kaplun
8

Votre question concerne le fonctionnement de php.

Prenons l' exemple de $ wpdb

$ wpdb est une variable globale bien connue.

Savez-vous quand il sera déclaré et attribué avec des valeurs?

Chaque page chargée , oui, chaque fois que vous visitez votre site wordpress.

De même, vous devez vous assurer que les variables que vous souhaitez globaliser seront déclarées et affectées avec les valeurs correspondantes à chaque page chargée.

Bien que je ne sois pas concepteur de thème, je peux dire que l'after_setup_theme est un crochet unique. il ne sera déclenché que lorsque le thème sera activé.

Si j'étais vous, j'utiliserai init ou d'autres crochets. Non, si j'étais vous, je n'utiliserais pas du tout de variables globales ...

Je ne suis vraiment pas bon pour expliquer les choses. Donc, vous devriez prendre un livre si vous voulez vous plonger dans PHP.

Jesse
la source
2

Vous pouvez toujours utiliser un modèle singleton via des getters statiques.

<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
la source