Cum să setezi și să folosești variabile globale? Sau de ce să nu le folosești deloc
ACTUALIZARE: Problema mea inițială a fost rezolvată, dar discuția s-a transformat într-o dezbatere valabilă despre de ce să nu folosim variabile globale, așa că actualizez întrebarea pentru a reflecta acest lucru. Soluția a fost <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?>
așa cum a sugerat @TomJNowell.
ACTUALIZARE 2: Acum funcționează exact cum doream. Dar încă folosesc scope-ul global și aș fi fericit să găsesc o metodă mai bună.
Încerc să configurez o serie de variabile globale pentru permalink-urile categoriilor, care să fie folosite în diverse locuri în tema mea. Motivul principal este pentru utilizarea atât în navigația principală, cât și într-o serie de sub-navigații care sunt alese în funcție de categoria în care se află postul curent. Aceasta nu este o temă pe care o voi lansa pentru utilizare de către alții, ci este construită pentru un scop foarte specific.
Iată cum le creez în prezent (am inclus doar câteva dintre variabile).
function set_global_nav_var()
{
//propunere
global $prop;
// Obține ID-ul unei categorii date
$category_id_prop = get_cat_ID( 'proposal' );
// Obține URL-ul acestei categorii
$category_link_prop = get_category_link( $category_id_prop );
$prop = '<a href="' .esc_url( $category_link_prop ). '" title="Proposal">Proposal</a>';
//Calvinball
global $cb;
// Obține ID-ul unei categorii date
$category_id_cb = get_cat_ID( 'calvinball' );
// Obține URL-ul acestei categorii
$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' );
Acum pot face <?php global $prop; echo $prop; ?>
în cele 4 locuri unde este nevoie și obțin întregul link pentru cod. Când acesta se schimbă, trebuie să-l modific doar într-un singur loc. Sunt deschis la alternative care nu implică scope-ul global.
Deși recomand cu tărie să evitați acest lucru și nu va accelera procesul, utilizarea dvs. este incorectă.
WordPress deja cachează aceste lucruri în cache-ul de obiecte/memorie, astfel încât nu trebuie să le preia de mai multe ori în aceeași cerere, nu este nevoie să stocați rezultatul și să-l refolosiți, WordPress face asta deja în mod implicit.
Este foarte probabil ca codul dvs. să ruleze mai încet ca urmare a acestei micro-optimizări, nu mai rapid!
Cum să Folosiți Variabile Globale
Când încercați să folosiți o variabilă globală, trebuie mai întâi să specificați cuvântul cheie global
. Ați specificat acest lucru aici când ați definit valoarea sa, dar în afara acelui scop, trebuie redeclarată ca o variabilă în scop global.
De exemplu, în functions.php
:
function test() {
global $hello;
$hello = 'salut lume';
}
add_action( 'after_setup_theme', 'test' );
În single.php
, acest lucru nu va funcționa:
echo $hello;
Deoarece $hello
este nedefinită. Totuși, acest lucru va funcționa:
global $hello;
echo $hello;
Desigur, nu ar trebui să faceți niciuna dintre acestea. WordPress deja încearcă să cacheze aceste lucruri în cache-ul de obiecte.
Dezavantaje și Pericole ale Variabilelor Globale
Nu veți observa nicio creștere a vitezei făcând acest lucru (s-ar putea să observați o mică scădere a vitezei), tot ce veți obține este complexitate suplimentară și necesitatea de a tasta o mulțime de declarații globale care nu sunt necesare.
De asemenea, veți întâmpina alte probleme:
- cod imposibil de testat
- cod care se comportă diferit de fiecare dată când rulează
- conflicte în numele variabilelor dintr-un spațiu de nume partajat
- bug-uri accidentale din cauza uitării de a declara
global
- lipsa totală de structură a stocării datelor din cod
- și multe altele
Ce Ar Trebui să Folosiți În Loc?
Ar fi mai bine să folosiți date structurate, cum ar fi obiecte sau injectare de dependențe, sau în cazul dvs., un set de funcții.
Iată 3 alternative:
Variabile Statice
Variabilele statice nu sunt bune, dar gândiți-vă la ele ca la verișoara ceva mai puțin rea a variabilelor globale. Variabilele statice sunt pentru variabilele globale, ceea ce pâinea acoperită cu noroi este pentru cianură.
De exemplu, iată o metodă de a face ceva similar prin variabile statice, de exemplu:
function functie_proasta( $new_hello='' ) {
static $hello;
if ( !empty( $new_hello ) ) {
$hello = $new_hello;
}
return $hello;
}
functie_proasta( 'telefon' );
echo functie_proasta(); // afișează telefon
functie_proasta( 'banana');
echo functie_proasta(); // afișează banana
Rețineți că există alte motive pentru a folosi variabile statice care nu sunt legate de cache și performanță, dar acestea au propriile dezavantaje. Este dificil, dacă nu imposibil, să scrieți teste adecvate pentru o funcție care folosește o variabilă statică și face depanarea mult mai grea, deoarece trebuie să țineți evidența valorii acelei variabile.
Într-un cod bun, o funcție pură face întotdeauna același lucru când i se dau aceiași parametri. Funcțiile pure sunt predictibile și ușor de testat. O funcție cu o variabilă statică nu poate fi niciodată o funcție pură.
Singleton-uri
Singleton-urile sunt obiecte care sunt create o singură dată și poate exista doar o singură instanță a acelui obiect. Sunt la fel de rele ca variabilele globale, doar cu o sintaxă diferită, au toate aceleași probleme ca variabilele statice și oferă aparența programării orientate pe obiecte fără niciunul dintre beneficii. Evitați-le.
Lectură suplimentară:
WP_Cache, Lucrul pe Care Ați Încercat să Îl Faceți Dar WordPress Îl Face Deja
Dacă chiar doriți să economisiți timp stocând date undeva pentru a le reutiliza, luați în considerare utilizarea sistemului WP_Cache
cu wp_cache_get
etc., de exemplu:
$value = wp_cache_get( 'hello' );
if ( false === $value ) {
// nu a fost găsit, setați valoarea implicită
wp_cache_set( 'hello', 'world' );
}
Acum valoarea va fi cache-uită pentru durata cererii de către WordPress, va apărea în instrumentele de depanare și, dacă aveți un cache de obiecte, va persista între cereri.
Înseamnă Asta Că Trebuie să Folosesc wp_cache_get
în Codul Meu?
Probabil nu, WordPress deja face asta automat pentru postări/utilizatori/meta-termeni/opțiuni/etc., așa că nu trebuie să stocați sau să preluați datele postărilor în acest fel. Doar folosiți funcțiile normale API și se va face automat în spate.
Notă laterală 1: Aș remarca că unii oameni încearcă să păstreze date în variabile globale între cereri, necunoscând faptul că PHP nu funcționează așa. Spre deosebire de o aplicație Node, fiecare cerere încarcă o copie nouă a aplicației, care apoi moare când cererea este finalizată. Din acest motiv, variabilele globale setate într-o cerere nu supraviețuiesc următoarei cereri.
Notă laterală 2: Judecând după întrebarea actualizată, variabilele globale nu vă oferă niciun câștig de performanță. Ar trebui să generați HTML-ul atunci când aveți nevoie de el și ar rula la fel de rapid, poate chiar puțin mai repede. Aceasta este o micro-optimizare.

Știu că e puțin nebunesc să folosesc scope-ul global, dar majoritatea, dacă nu toate aceste variabile vor fi folosite pe fiecare pagină. Sunt deschis la idei mai bune. Am să editez întrebarea pentru a-mi face intenția puțin mai clară. Apropo, funcționează perfect când fac <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?>
conform sugestiei tale. Mulțumesc!

Ah, dacă soluția mea funcționează, poți să o marchezi ca acceptată? Variabilele tale globale sunt la fel de rapide ca și apelul original, poți încerca să folosești funcții ca să nu mai fie nevoie să scrii 2 linii, mai bine, un singleton, și mai bine, să faci totul dinamic și într-un template part inclus prin get_template_part

Am marcat ca acceptată deoarece asta fac acum, deși aș putea merge pe una dintre strategiile sugerate de @MarkKaplun mai jos. Folosirea get_template_part() este o idee interesantă, dar nu sunt sigur că vreau să am un director plin cu fișiere scurte de genul acesta...

oooh nu nu, nu ai vrea un fișier pentru fiecare categorie, ai vrea doar unul care preia numele categoriei curente și îl folosește. Nu ar trebui să fie nevoie să hardcodezi nimic, imaginează-ți chinul de a hardcoda totul

Am pus codul în child-functions.php care este activ. Dar nu pot accesa variabila într-un fișier php-include pe care îl apelez dintr-un post "normal" generat din baza de date. Te rog să mă sfătuiești, ce greșesc? (O definesc ca global, bineînțeles.)

Trebuie să declari global
de fiecare dată când îl folosești. Nu este un lucru pe care îl declari o dată și funcționează peste tot, trebuie să îl folosești de fiecare, și în fiecare, dată, fără excepții de niciun fel. Dar, așa cum spun în întrebarea mea, variabilele globale sunt o practică proastă, problematică și nu sunt soluția pe care o cauți pentru problema ta. Chiar și răul care sunt singleton-ii ar fi o soluție mai bună

Mulțumesc, apreciez comentariul tău. L-am declarat și l-am folosit, dar undeva trebuie să fi făcut o greșeală. Pur și simplu nu a funcționat pentru mine. Am încercat diferite abordări. În final aceasta a funcționat. Sunt de acord cu ceea ce spui despre globale, dar uneori este nevoie de o soluție rapidă pentru un site personal. Mulțumesc.

Nu folosiți variabile globale, atât de simplu.
De ce să nu folosiți globale
Deoarece utilizarea variabilelor globale face mai dificilă întreținerea software-ului pe termen lung.
- O variabilă globală poate fi declarată oriunde în cod sau nicăieri, prin urmare nu există un loc în care să poți căuta în mod instinctiv comentarii despre scopul acesteia
- În timp ce citești codul, de obicei presupui că variabilele sunt locale pentru funcție și nu înțelegi că modificarea valorii lor într-o funcție poate avea un impact la nivelul întregului sistem.
- Dacă nu prelucrează input, funcțiile ar trebui să returneze aceeași valoare/ieșire atunci când sunt apelate cu aceiași parametri. Utilizarea variabilelor globale într-o funcție introduce parametri suplimentari care nu sunt documentați în declarația funcției.
- Variabilele globale nu au niciun constructor de inițializare specific și, prin urmare, nu poți fi niciodată sigur când poți accesa valoarea globală și nu primești nicio eroare când încerci să accesezi variabila globală înainte de inițializare.
- Cineva altcineva (un plugin, poate) ar putea folosi variabile globale cu același nume, stricându-ți codul sau tu stricându-i al lui, în funcție de ordinea de inițializare.
Nucleul WordPress are mult prea multe utilizări ale variabilelor globale. În timp ce încerci să înțelegi cum funcționează funcțiile și hook-urile de bază precum the_content
, realizezi brusc că variabila $more
nu este locală, ci globală și trebuie să cauți în toate fișierele de bază pentru a înțelege când este setată la true.
Deci, ce poți face atunci când încerci să eviți copierea și lipirea mai multor linii de cod în loc să stochezi rezultatul primei rulări într-o variabilă globală? Există mai multe abordări, funcționale și OOP.
Funcția sweetener. Este pur și simplu un wrapper/macro pentru a salva copierea/lipirea
// input: $id - ID-ul categoriei
// returnează: valoarea foo2 a categoriei
function notaglobal($id) {
$a = foo1($id);
$b = foo2($a);
return $b;
}
Beneficiile sunt că acum există o documentație despre ce face fostul global și ai un punct evident pentru depanare atunci când valoarea returnată nu este cea pe care o așteptai.
Odată ce ai un sweetener, este ușor să cache-ui rezultatul dacă este necesar (fă-o doar dacă descoperi că această funcție durează mult timp pentru a executa)
function notaglobal($id) {
static $cache;
if (!isset($cache)) {
$a = foo1($id);
$b = foo2($a);
$cache = $b;
}
return $cache;
}
Aceasta îți oferă același comportament ca o variabilă globală, dar cu avantajul că ai o inițializare asigurată de fiecare dată când o accesezi.
Poți avea modele similare cu OOP. Consider că OOP de obicei nu adaugă nicio valoare în plugin-uri și teme, dar aceasta este o discuție diferită
class notaglobal {
var latestfoo2;
__constructor($id) {
$a = foo1($id);
$this->latestfoo2 = foo2($a)
}
}
$v = new notaglobal($cat_id);
echo $v->latestfoo2;
Acesta este un cod mai stângaci, dar dacă ai mai multe valori pe care ai vrea să le precalculezi pentru că sunt întotdeauna folosite, aceasta poate fi o cale de urmat. Practic, acesta este un obiect care conține toate variabilele tale globale într-un mod organizat. Pentru a evita să faci o instanță a acestui obiect globală (vrei doar o singură instanță, altfel recalculezi valorile), ai putea dori să folosești un model singleton (unii oameni susțin că este o idee proastă, YMMV)
Nu îmi place să accesez direct un atribut al unui obiect, așa că în codul meu va fi încapsulat mai mult
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);

Te rog, nu țipa. Poți să explici de ce și să oferi o sursă sau o citare?

Cred că ai înțeles greșit răspunsul. Dacă nu încerca să facă o optimizare prematură prin stocarea valorilor în variabile globale, codul lui ar fi funcționat. Țipatul este pentru că respectarea principiilor de bază stabilite în dezvoltarea de software este ceva care nu poate fi subliniat suficient. Oamenii care nu înțeleg aceste principii de bază (disponibile la un simplu search pe Google) nu ar trebui să răspândească cod pe internet.

Salut, Mark, îmi cer scuze, comentariul meu a fost la fel de scurt ca răspunsul tău și ar fi trebuit să mă exprim mai clar: 1) IMO, bold este suficient pentru a sublinia un punct. 2) Deși uneori nu mai este nimic de adăugat, sunt suspicios față de răspunsurile cu o singură linie: Este acceptabil să postezi un răspuns cu o singură linie sau ar fi mai bine să fie comentarii?

da, abia după ce am postat am realizat că ar fi trebuit să folosesc bold. voi repara acest aspect

IMO acesta este un răspuns, oamenii care ajung aici de pe Google ar trebui să vadă că este o idee proastă să te gândești chiar din start să folosești variabile globale.

Nu este suficient să spui să nu faci X, trebuie să explici de ce altfel pari că o spui la întâmplare

@MarkKaplun Ce ai face în schimb pentru a evita să scrii același lucru de mai multe ori, iar apoi să fii nevoit să modifici fiecare manual dacă orice parte se schimbă?

@TomJNowell, mi se pare amuzant că eu am fost singurul care a dat vot negativ întrebării în sine, deoarece era evident în afara domeniului WASE. Nu am văzut valoarea în a dezvolta un subiect care nu ar fi trebuit să fie inițiat aici deloc.

@MarkKaplun Minunat. Prima ta soluție sună cel mai bine. Voi experimenta cu caching, care probabil va fi necesar. Nu sunt sigur de ce aceasta este în afara domeniului de aplicare al acestui stackexchange? Întrebarea este despre PHP, dar se dovedește că are totul de-a face cu modul în care WordPress gestionează globalele. De asemenea, cazul este specific meniurilor de navigare WordPress.

"WordPress core are mult prea multă utilizare a globalelor." Aș spune că WordPress să aibă orice globale este mult prea mult, dar asta este doar părerea mea.

@MarkKaplun mulțumesc pentru abordarea ta funcțională. În cazul în care o adoptăm, ai putea face o actualizare să ne arăți cum ar trebui să arate cu o valoare de rezervă $ID, dacă din anumite motive nu există, nu a fost setată sau nu este un întreg pozitiv?

Întrebarea ta este legată de modul în care funcționează PHP.
Să luăm $wpdb ca exemplu.
$wpdb este o variabilă globală binecunoscută.
Știi când este declarată și inițializată cu valori?
La fiecare încărcare de pagină, da, de fiecare dată când vizitezi site-ul tău WordPress.
În mod similar, trebuie să te asiguri că acele variabile pe care dorești să le globalizezi vor fi declarate și inițializate cu valorile corespunzătoare la fiecare încărcare de pagină.
Deși nu sunt designer de teme, pot spune că after_setup_theme
este un hook care se declanșează o singură dată. Acesta va fi activat doar când tema este activată.
Dacă aș fi în locul tău, aș folosi init
sau alte hook-uri. Nu, dacă aș fi în locul tău, nu aș folosi deloc variabile globale...
Nu sunt foarte bun în a explica lucrurile. Așadar, ar fi bine să iei o carte dacă vrei să aprofundezi PHP.

Puteți utiliza întotdeauna un model singleton prin intermediul getter-ilor statici.
<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' => 'Propunere', 'text' => 'Propunere' ),
'calvinball' => array( 'title' => 'Calvinball', 'text' => 'Calvinball' ),
);
}
public function get_nav_prop ( $term, $prop = false )
{
$o = self::instance();
if ( ! isset( $o->props[$term] ) ) { return false; }
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
