Jak ustawić i używać zmiennych globalnych? Albo dlaczego w ogóle ich nie używać

27

AKTUALIZACJA: Moje oryginalne pytanie zostało rozwiązane, ale zamienia się w ważną dyskusję na temat tego, dlaczego nie używać zmiennych globalnych, dlatego aktualizuję pytanie, aby to odzwierciedlić. Rozwiązanie było takie, <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?>jak sugerował @TomJNowell.

AKTUALIZACJA 2: Teraz robię dokładnie to, co chciałem. Ale nadal używam globalnego zasięgu i chętnie znajdę lepszy sposób.

Próbuję skonfigurować całą masę zmiennych globalnych dla bezpośrednich łączy do kategorii, które będą używane w różnych miejscach mojego motywu. Głównym tego powodem jest użycie zarówno w głównej nawigacji, jak i w szeregu nawigacji podrzędnych, które są wybierane na podstawie kategorii, w której znajduje się bieżący post. To nie jest temat, który udostępnię innym osobom, ale został zbudowany w jednym, bardzo określonym celu.

Tak właśnie je teraz tworzę (wkleiłem tylko kilka zmiennych).

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

Mogę teraz zrobić <?php global $prop; echo $prop; ?>4 miejsca i odzyskać cały link do kodu. Kiedy te zmiany muszę zmienić tylko w jednym miejscu. Jestem otwarty na alternatywy, które nie obejmują zasięgu globalnego.

JPollock
źródło
1
Który link wykonuje to polecenie echo esc_url ($ category_link_prop); wyświetla? Jaki jest twój oczekiwany link?
Vinod Dalvi
1
Dlaczego nie używałbyś po prostu „get_cat_ID (****)” tam, gdzie kiedykolwiek planowałeś używać zmiennej globalnej. Wątpię, czy sposób, w jaki to robisz, miałby jakąkolwiek przewagę prędkości. Z punktu widzenia czytelności „get_cat_ID (****)” wygrywa.
Chris Strutton
1
Czy potrafisz przeredagować? Przeczytałem twoje pytanie i nadal nie jestem pewien, co chcesz zrobić i dlaczego chcesz to zrobić. Moja ogólna rada byłaby taka, aby nie używać zmiennych globalnych i nie zanieczyszczać globalnego zasięgu
Tom J Nowell
1
ten brzmi trochę jak X / Y problem . być może powinieneś wykonać kopię zapasową i wyjaśnić dokładnie, jaki jest twój pożądany wynik. Jestem pewien, że istnieje o wiele bardziej eleganckie rozwiązanie niż ustawienie szeregu globalnych zmiennych, aby następnie odwoływać się do nich na stałe w nawigacjach gdzie indziej
Milo
2
utwórz funkcję, która wyświetla menu na podstawie przekazanego mu kontekstu, dzięki czemu możesz zachować całą logikę menu i powiązane z nią zmienne w jednym miejscu.
Milo

Odpowiedzi:

21

Chociaż zdecydowanie odradzam to i nie przyspieszy to, twoje użycie jest nieprawidłowe.

Kiedy próbujesz użyć globalnego, musisz najpierw określić globalne słowo kluczowe. Podano go tutaj podczas definiowania jego wartości, ale poza tym zakresem należy go ponownie zadeklarować jako globalną zmienną zakresu.

np. w functions.php:

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

W single.php to nie zadziała:

echo $hello;

Ponieważ $ hello jest niezdefiniowana. To jednak będzie działać:

global $hello;
echo $hello;

Oczywiście nie powinieneś tego robić. WordPress już próbuje buforować te rzeczy w pamięci podręcznej obiektów. Dzięki temu nie zobaczysz żadnego wzrostu prędkości (możesz zauważyć niewielki spadek prędkości), wszystko co dostaniesz to dodatkowa złożoność i potrzeba wpisania wielu globalnych deklaracji, które nie są konieczne.

Lepiej byłoby użyć danych strukturalnych, takich jak wstrzyknięcie obiektów lub zależności, lub w twoim przypadku zestaw funkcji.

Na przykład, tutaj jest sposób na zrobienie czegoś podobnego za pomocą zmiennych statycznych (nadal złe z tych samych powodów, ale tylko trochę mniej i łatwiejsze do wpisania) np.

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

Jeśli naprawdę chcesz zaoszczędzić czas, przechowując dane w celu ponownego użycia, rozważ użycie WP_Cachesystemu z wp_cache_getitp

Tom J Nowell
źródło
Wiem, że używanie globalnego zasięgu jest trochę szalone, ale większość, jeśli nie wszystkie z tych zmiennych, zostaną użyte na każdej stronie. Jestem otwarty na lepsze pomysły. Mam zamiar edytować pytanie, aby moje zamiary były nieco jaśniejsze. BTW działa idealnie dobrze, gdy robię <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?>zgodnie z twoją sugestią. Dzięki!
JPollock
2
Ach, jeśli moje rozwiązanie zadziała, czy możesz oznaczyć jako zaakceptowane? Twoje zmienne globalne są tak samo szybkie, jak wykonanie oryginalnego wywołania, możesz zamiast tego spróbować użyć funkcji, więc nie musisz wpisywać 2 wierszy, jeszcze lepiej, singletonu, jeszcze lepiej, spraw, aby wszystko to było dynamiczne część szablonu zawarta w get_template_part
Tom J Nowell
Oznaczone jako zaakceptowane jako to, co robię teraz, chociaż mogę pójść z jedną ze strategii, którą @ MarkKaplun sugeruje poniżej. Korzystanie z get_template_part () jest interesującym pomysłem, ale nie jestem pewien, czy chcę mieć
katalog
oooh nie nie nie chciałbyś pliku dla każdej kategorii, chciałbyś tylko tego, który pobiera bieżącą nazwę kategorii i używa jej. Nie powinieneś kodować niczego na stałe, wyobraź sobie kłopot związany z kodowaniem wszystkiego
Tom J Nowell
Wstawiam kod do mojego child-functions.php, który jest aktywny. Ale nie mogę uzyskać dostępu do zmiennej w pliku php-include, który wywołuję z „normalnego” postu wygenerowanego przez bazę danych. Proszę mi doradzić, co mam zrobić źle? (Oczywiście określam to jako globalne.)
ycc_swe
19

Nie używaj zmiennych globalnych , tak proste.

Dlaczego nie korzystać z globałów

Ponieważ użycie globałów utrudnia utrzymanie oprogramowania w dłuższej perspektywie.

  • Globalny może być zadeklarowany w dowolnym miejscu w kodzie lub w ogóle, dlatego nie ma miejsca, w którym można instynktownie spojrzeć, aby znaleźć komentarz na temat tego, do czego globalny jest używany
  • Podczas czytania kodu zazwyczaj zakładasz, że zmienne są lokalne dla funkcji i nie rozumiesz, że zmiana ich wartości w funkcji może spowodować zmianę w całym systemie.
  • Jeśli nie obsługują danych wejściowych, funkcje powinny zwracać tę samą wartość / dane wyjściowe, gdy są wywoływane z tymi samymi parametrami. Zastosowanie globałów w funkcji wprowadza dodatkowe parametry, które nie są udokumentowane w deklaracji funkcji.
  • globals nie mają żadnej konkretnej konstrukcji inicjującej i dlatego nigdy nie możesz być pewien, kiedy możesz uzyskać dostęp do wartości globalnej, i nie pojawia się żaden błąd podczas próby uzyskania dostępu do globalnej przed inicjalizacją.
  • Ktoś inny (być może wtyczka) może użyć globałów o tej samej nazwie, rujnując kod lub rujnując go w zależności od kolejności inicjalizacji.

Rdzeń WordPress w znacznym stopniu wykorzystuje globały. Próbując zrozumieć, jak działają podstawowe funkcje the_content, nagle zdajesz sobie sprawę, że $morezmienna nie jest lokalna, ale globalna i musisz przeszukać całe podstawowe pliki, aby zrozumieć, kiedy jest ustawiona na wartość true.

Co więc można zrobić, gdy próbujesz zatrzymać kopiowanie i wklejanie kilku wierszy kodu zamiast przechowywać wynik pierwszego uruchomienia w globalnym? Istnieje kilka podejść, funkcjonalnych i OOP.

Funkcja słodzika. Jest to po prostu opakowanie / makro do zapisywania kopii / wklejania

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

Korzyści są takie, że teraz jest dokumentacja tego, co robi poprzedni globalny, i masz oczywisty punkt do debugowania, gdy zwracana wartość nie jest tą, której się spodziewasz.

Po otrzymaniu słodzika łatwo jest buforować wynik w razie potrzeby (zrób to tylko wtedy, gdy odkryjesz, że wykonanie tej funkcji zajmuje dużo czasu)

function notaglobal($id) {
  static $cache;

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

Daje to takie samo zachowanie globalne, ale ma tę zaletę, że zapewnia bezpieczną inicjalizację za każdym razem, gdy się do niego uzyskujesz.

Możesz mieć podobne wzorce z OOP. Uważam, że OOP zwykle nie dodaje żadnej wartości do wtyczek i motywów, ale to inna dyskusja

class notaglobal {
   var latestfoo2;

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

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

Jest to nieporadny kod, ale jeśli masz kilka wartości, które chcesz wstępnie obliczyć, ponieważ są one zawsze używane, może to być dobry sposób. Zasadniczo jest to obiekt, który zawiera wszystkie twoje globale w zorganizowany sposób. Aby uniknąć przekształcenia instancji tego obiektu w globalną (chcesz tylko jedną instancję, w przeciwnym razie przeliczasz wartości), możesz użyć wzorca singletonu (niektórzy twierdzą, że to zły pomysł, YMMV)

Nie lubię bezpośrednio uzyskiwać dostępu do atrybutu obiektu, więc w moim kodzie będzie to więcej

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
źródło
7
Proszę, nie krzycz . Czy chcesz wyjaśnić, dlaczego i podać jakieś cytowanie?
brasofilo,
Myślę, że źle zrozumiałeś odpowiedź. Gdyby nie próbował dokonać wczesnej optymalizacji poprzez przechowywanie wartości w zmiennych globalnych, jego kod by zadziałał. Krzyczeć, ponieważ przestrzeganie podstawowych ustalonych zasad tworzenia oprogramowania jest czymś, czego nie można wystarczająco podkreślić. Ludzie, którzy nie rozumieją tych podstawowych zasad (dostępnych w lokalnym Google), nie powinni rozsyłać kodu w sieci.
Mark Kaplun,
1
IMO to odpowiedź, ludzie, którzy przychodzą tutaj z Google, powinni zobaczyć, że złym pomysłem jest nawet natychmiastowe użycie globali.
Mark Kaplun,
6
Nie wystarczy powiedzieć „nie rób X”, musisz wyjaśnić, dlaczego lub wyglądasz, jakbyś mówił kaprys
Tom J Nowell
1
@TomJNowell, wydaje mi się zabawne, że jako jedyny głosowałem samo pytanie, ponieważ było to oczywiście poza zakresem WASE. Nie widziałem wartości rozwijania tematu, który w ogóle nie powinien był się tutaj zaczynać.
Mark Kaplun
8

Twoje pytanie dotyczy działania php.

Weź jako przykład $ wpdb

$ wpdb jest znaną zmienną globalną.

Czy wiesz, kiedy zostanie zadeklarowany i przypisany wartościom?

Każda strona ładowana , tak, za każdym razem, gdy odwiedzasz witrynę Wordpress.

Podobnie musisz upewnić się, że zmienne, które chcesz zglobalizować, zostaną zadeklarowane i przyporządkowane do nich odpowiednie wartości przy każdej załadowanej stronie.

Chociaż nie jestem projektantem motywów, mogę powiedzieć, że after_setup_theme to jednorazowy haczyk. zostanie aktywowany tylko po aktywowaniu motywu.

Gdybym był tobą, użyłbym init lub innych haków. Nie, gdybym był tobą, w ogóle nie używałbym zmiennych globalnych ...

Naprawdę nie jestem dobry w wyjaśnianiu rzeczy. Powinieneś więc wybrać książkę, jeśli chcesz zagłębić się w PHP.

Jesse
źródło
2

Zawsze możesz użyć wzorca singletonu za pomocą statycznych mechanizmów pobierających.

<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
źródło