Wie setze und verwende ich globale Variablen? Oder warum man sie überhaupt nicht benutzt

27

UPDATE: Meine ursprüngliche Frage wurde gelöst, aber dies wird zu einer gültigen Diskussion darüber, warum keine globalen Variablen verwendet werden sollten. Ich aktualisiere die Frage daher, um dies widerzuspiegeln. Die Lösung war <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?>wie @TomJNowell vorgeschlagen.

UPDATE 2: Ich habe jetzt genau das getan, was ich wollte. Aber ich benutze immer noch globale Möglichkeiten und würde gerne einen besseren Weg finden.

Ich versuche, eine ganze Reihe globaler Variablen für die Permalinks zu Kategorien einzurichten, die an verschiedenen Stellen in meinem Thema verwendet werden sollen. Der Hauptgrund dafür ist die Verwendung sowohl in der Hauptnavigation als auch in einer Reihe von Unternavigationen, die anhand der Kategorie ausgewählt werden, in der sich der aktuelle Beitrag befindet. Dies ist kein Thema, das ich für die Verwendung durch andere freigeben werde. sondern ist für einen ganz bestimmten Zweck gebaut.

So erstelle ich sie gerade (ich habe nur einige Variablen eingefügt).

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' );

Ich kann jetzt an den <?php global $prop; echo $prop; ?>4 Stellen, die gehen und den gesamten Link für den Code zurückbekommen. Wenn sich das ändert, muss ich es nur an einer Stelle ändern. Ich bin offen für Alternativen, die nicht den globalen Geltungsbereich betreffen.

JPollock
quelle
1
Welchen Link gibt diese Anweisung als Echo für esc_url ($ category_link_prop) aus? zeigt? Was ist Ihr erwarteter Link?
Vinod Dalvi
1
Warum sollten Sie nicht einfach 'get_cat_ID (****)' verwenden, wo immer Sie die globale Variable verwenden wollten? Ich bezweifle, dass es einen Geschwindigkeitsvorteil geben würde, wie Sie es tun. Aus Sicht der Lesbarkeit gewinnt 'get_cat_ID (****)' zweifellos.
Chris Strutton
1
Kannst du das umformulieren? Ich habe Ihre Frage gelesen und bin mir immer noch nicht sicher, was Sie tun möchten und warum Sie es tun möchten. Mein allgemeiner Rat wäre, keine globalen Variablen zu verwenden und den globalen Geltungsbereich nicht zu verschmutzen
Tom J Nowell
1
Das klingt ein bisschen wie ein X / Y-Problem . Vielleicht solltest du genau erklären, was dein gewünschtes Ergebnis ist. Ich bin mir sicher, dass es eine weitaus elegantere Lösung gibt, als ein paar globale Variablen zu setzen, um dann nur Verweise auf sie in einem Navi anderswo fest zu codieren
Milo
2
Erstellen Sie eine Funktion, die Ihr Menü basierend auf dem Kontext ausgibt, den Sie an das Menü übergeben. Auf diese Weise können Sie die gesamte Menülogik und die zugehörigen Variablen an einem Ort zusammenfassen.
Milo

Antworten:

21

Ich rate zwar dringend davon ab und es wird die Dinge nicht beschleunigen, aber Ihre Verwendung ist falsch.

Wenn Sie versuchen, ein globales Schlüsselwort zu verwenden, müssen Sie zuerst das globale Schlüsselwort angeben. Sie haben es hier bei der Definition des Werts angegeben, aber außerhalb dieses Bereichs muss es als globale Bereichsvariable neu deklariert werden.

zB in functions.php:

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

In single.php funktioniert das nicht:

echo $hello;

Weil $ hello undefiniert ist. Dies jedoch wird funktionieren:

global $hello;
echo $hello;

Natürlich solltest du beides nicht tun. WordPress versucht bereits, diese Dinge im Objekt-Cache zwischenzuspeichern. Sie werden dabei keine Geschwindigkeitssteigerung bemerken (es kann sein, dass die Geschwindigkeit geringfügig abnimmt). Sie werden lediglich zusätzliche Komplexität und die Notwendigkeit erhalten, viele globale Deklarationen einzugeben, die nicht erforderlich sind.

Sie sollten strukturierte Daten wie Objekte oder Abhängigkeitsinjektionen oder in Ihrem Fall eine Reihe von Funktionen verwenden.

Zum Beispiel ist hier ein Mittel, um etwas Ähnliches über statische Variablen zu tun (immer noch schlecht aus den gleichen Gründen, aber nur ein kleines bisschen weniger und einfacher zu tippen), z

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

Wenn Sie wirklich Zeit sparen möchten, indem Sie Daten an einem Ort speichern, den Sie wiederverwenden möchten, sollten Sie das WP_CacheSystem mit wp_cache_getusw. Verwenden

Tom J Nowell
quelle
Ich weiß, dass es ein bisschen verrückt ist, den globalen Bereich zu verwenden, aber die meisten, wenn nicht alle dieser Variablen, werden auf jeder Seite verwendet. Ich bin offen für bessere Ideen. Ich werde die Frage bearbeiten, um meine Absicht ein wenig klarer zu machen. Übrigens funktioniert es einwandfrei, wenn ich <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?>nach Ihrem Vorschlag mache . Vielen Dank!
JPollock
2
Ah, wenn meine Lösung funktioniert, können Sie sie als akzeptiert markieren? Ihre globalen Variablen sind genauso schnell wie der ursprüngliche Aufruf. Sie können auch versuchen, stattdessen Funktionen zu verwenden, damit Sie nicht zwei Zeilen eingeben müssen, noch besser, einen Singleton, noch besser, machen Sie all das dynamisch und in einem Vorlage Teil über get_template_part enthalten
Tom J Nowell
Als akzeptiert markiert, was ich gerade mache, obwohl ich vielleicht eine der Strategien wählen möchte, die @MarkKaplun unten vorschlägt. Die Verwendung von get_template_part () ist eine interessante Idee, aber ich bin nicht sicher, ob ich ein Verzeichnis voller
solcher
oooh nein nein du würdest nicht eine Datei für jede Kategorie wollen, du würdest nur diejenige wollen, die den aktuellen Kategorienamen ergreift und diesen verwendet. Sie sollten nichts hartcodieren müssen, stellen Sie sich den Aufwand vor, alles hartcodieren zu müssen
Tom J Nowell
Ich habe den Code in meine child-functions.php geschrieben, die aktiv ist. Ich kann jedoch nicht auf die Variable in einer PHP-Include-Datei zugreifen, die ich von einem "normalen" datenbankgenerierten Beitrag aus aufrufe. Bitte raten Sie mir, was mache ich falsch? (Ich definiere es natürlich als global.)
ycc_swe
19

Verwenden Sie keine so einfachen globalen Variablen .

Warum nicht Globals verwenden?

Weil die Verwendung von Globals die langfristige Wartung der Software erschwert.

  • Ein Global kann irgendwo im Code oder nirgendwo anders deklariert werden, daher gibt es keinen Ort, an dem Sie instinktiv nachsehen können, um einen Kommentar darüber zu finden, wofür das Global verwendet wird
  • Wenn Sie Code lesen, gehen Sie normalerweise davon aus, dass Variablen für die Funktion lokal sind, und verstehen nicht, dass die Änderung ihres Werts in einer Funktion eine systemweite Änderung haben kann.
  • Wenn sie keine Eingabe verarbeiten, sollten Funktionen denselben Wert / dieselbe Ausgabe zurückgeben, wenn sie mit denselben Parametern aufgerufen werden. Durch die Verwendung von Globalen in einer Funktion werden zusätzliche Parameter eingeführt, die in der Funktionsdeklaration nicht dokumentiert sind.
  • Globals haben kein spezielles Initialisierungskonstrukt. Daher können Sie nie sicher sein, wann Sie auf den Wert des Globals zugreifen können, und Sie erhalten keine Fehlermeldung, wenn Sie versuchen, vor der Initialisierung auf das Globale zuzugreifen.
  • Jemand anderes (vielleicht ein Plugin) könnte Globals mit demselben Namen verwenden, wodurch Ihr Code ruiniert wird, oder Sie ruinieren ihn, abhängig von der Initialisierungsreihenfolge.

Der WordPress-Kern hat viel zu viel Gebrauch von Globalen. Beim Versuch zu verstehen, wie grundlegende Funktionen the_contentfunktionieren, stellen Sie plötzlich fest, dass die $moreVariable nicht lokal, sondern global ist. Sie müssen die gesamten Kerndateien durchsuchen, um zu verstehen, wann sie auf true gesetzt ist.

Was kann also getan werden, wenn versucht wird, das Kopieren und Einfügen mehrerer Codezeilen zu beenden, anstatt das Ergebnis der ersten Ausführung in einem globalen Code zu speichern? Es gibt verschiedene Ansätze, funktionale und OOP.

Die Süßstofffunktion. Es ist einfach ein Wrapper / Makro zum Speichern des Copy / Paste

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

Die Vorteile sind, dass es jetzt eine Dokumentation zu den Aufgaben des früheren globalen Systems gibt und Sie einen offensichtlichen Punkt für das Debuggen haben, wenn der zurückgegebene Wert nicht der erwartete Wert ist.

Sobald Sie einen Süßstoff haben, können Sie das Ergebnis bei Bedarf einfach zwischenspeichern (tun Sie dies nur, wenn Sie feststellen, dass die Ausführung dieser Funktion lange dauert).

function notaglobal($id) {
  static $cache;

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

Auf diese Weise haben Sie dasselbe Verhalten wie bei einem globalen Server, haben jedoch den Vorteil, dass Sie bei jedem Zugriff eine sichere Initialisierung haben.

Sie können ähnliche Muster mit OOP haben. Ich finde, dass OOP in Plugins und Themes normalerweise keinen Mehrwert bringt, aber das ist eine andere Diskussion

class notaglobal {
   var latestfoo2;

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

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

Dies ist ein umständlicher Code, aber wenn Sie mehrere Werte haben, die Sie vorberechnen möchten, weil sie immer verwendet werden, kann dies ein langer Weg sein. Grundsätzlich ist dies ein Objekt, das alle Ihre globalen Elemente auf organisierte Weise enthält. Um zu vermeiden, dass eine Instanz dieses Objekts global wird (Sie möchten nur eine Instanz, ansonsten berechnen Sie die Werte neu), möchten Sie möglicherweise ein Singleton-Muster verwenden (einige Leute behaupten, es sei eine schlechte Idee, YMMV).

Ich greife nicht gerne direkt auf ein Objektattribut zu, daher verzerrt es in meinem Code weiter

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
quelle
7
Bitte, schrei nicht . Darf ich erklären, warum und was ich zitiere?
Brasofilo
Ich denke, Sie haben die Antwort falsch verstanden. Wenn er nicht versucht hätte, eine frühe Optimierung durch Speichern von Werten in globalen Variablen durchzuführen, hätte sein Code funktioniert. Das Schreien ist, weil das Befolgen der grundlegenden Prinzipien der Softwareentwicklung nicht genug betont werden kann. Personen, die dieses Grundprinzip nicht verstehen (erhältlich bei Ihrem lokalen Google), sollten keinen Code über das Internet verbreiten.
Mark Kaplun
1
IMO, dies ist eine Antwort. Leute, die von Google hierher kommen, sollten sehen, dass es eine schlechte Idee ist, sofort über die Verwendung von Globalen nachzudenken.
Mark Kaplun
6
Es ist nicht genug zu sagen, nicht X zu tun, du musst erklären, warum oder du siehst aus, als würdest du es aus einer Laune heraus sagen
Tom J Nowell
1
@ TomJNowell, ich finde es lustig, dass ich der einzige war, der die Frage selbst abstimmte, da sie offensichtlich außerhalb des Geltungsbereichs von WASE lag. Ich habe nicht gesehen, wie wichtig es ist, ein Thema zu erweitern, mit dem hier überhaupt nicht begonnen werden sollte.
Mark Kaplun
8

Ihre Frage beschäftigt sich mit der Funktionsweise von PHP.

Nehmen Sie als Beispiel $ wpdb

$ wpdb ist eine bekannte globale Variable.

Wissen Sie, wann es deklariert und mit Werten belegt wird?

Jede Seite wird geladen , ja, jedes Mal, wenn Sie Ihre WordPress-Site besuchen.

Ebenso müssen Sie sicherstellen, dass die Variablen, die globalisiert werden sollen, deklariert und auf jeder geladenen Seite mit entsprechenden Werten belegt werden.

Obwohl ich kein Theme-Designer bin, kann ich sagen, dass after_setup_theme ein einmaliger Hook ist. Es wird nur ausgelöst, wenn das Thema aktiviert ist.

Wenn ich du wäre, würde ich Init oder andere Haken verwenden. Nein, wenn ich Sie wäre, würde ich überhaupt keine globalen Variablen verwenden ...

Ich bin wirklich nicht gut darin, Dinge zu erklären. Sie sollten sich also ein Buch zulegen, wenn Sie sich mit PHP befassen möchten.

Jesse
quelle
2

Sie können immer ein Singleton-Muster über statische Getter verwenden.

<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
quelle