Como definir e usar variáveis ​​globais? Ou por que não usá-los?

27

ATUALIZAÇÃO: Minha pergunta original foi resolvida, mas isso está se transformando em uma discussão válida sobre por que não usar variáveis ​​globais, por isso estou atualizando a questão para refletir isso. A solução foi <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?>como o @TomJNowell sugeriu.

ATUALIZAÇÃO 2: Agora eu faço exatamente o que eu queria. Mas ainda estou usando o escopo global e ficaria feliz em encontrar uma maneira melhor.

Estou tentando configurar um monte de variáveis ​​globais para os links permanentes para categorias a serem usadas em vários lugares no meu tema. A principal razão para isso é o uso na navegação principal e em uma série de sub-navegações escolhidas com base em qual categoria a postagem atual se encontra. Esse não é um tema que lançarei para uso por outras pessoas, mas é construído para um propósito muito específico.

É assim que os estou criando atualmente (colei apenas algumas das variáveis).

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

Agora eu posso fazer <?php global $prop; echo $prop; ?>int ele 4 lugares que vai e voltar todo o link para o código. Quando isso muda, só preciso mudar em um só lugar. Estou aberto a alternativas que não envolvem o escopo global.

JPollock
fonte
11
Qual link esta declaração echo esc_url ($ category_link_prop); exibe? Qual é o seu link esperado?
Vinod Dalvi
11
Por que você não usaria 'get_cat_ID (****)' sempre que planejou usar a variável global. Duvido que haja alguma vantagem de velocidade na maneira como você faz isso. Do ponto de vista da legibilidade, 'get_cat_ID (****)' ganha as mãos para baixo.
precisa saber é o seguinte
11
Você pode reformular? Eu li sua pergunta e ainda não tenho certeza do que você quer fazer e por que deseja fazê-lo. Meu conselho geral seria não usar variáveis globais, e não poluir o escopo global
Tom J Nowell
11
este faz soar um pouco como um de X / Y problema . talvez você deva fazer backup e explicar exatamente qual é o resultado desejado. Estou certo de que há uma solução muito mais elegante do que a criação de um grupo de vars globais para referências em seguida, basta codificar a eles em um nav em outros lugares
Milo
2
crie uma função que produza seu menu com base no contexto que você passa para ele, para que você possa manter toda a lógica do menu e os vars associados encapsulados em um só lugar.
Milo

Respostas:

21

Embora eu recomendo fortemente isso, e isso não acelera as coisas, seu uso está incorreto.

Ao tentar usar um global, você deve especificar a palavra-chave global primeiro. Você o especificou aqui ao definir seu valor, mas fora desse escopo, ele precisa ser redeclarado como uma variável de escopo global.

por exemplo, em functions.php:

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

No single.php, isso não funciona:

echo $hello;

Porque $ hello é indefinido. No entanto, isso funcionará:

global $hello;
echo $hello;

Claro que você não deve fazer nenhum. O WordPress já tenta armazenar essas coisas no cache de objetos. Você não verá nenhum aumento de velocidade fazendo isso (você pode ver uma pequena diminuição de velocidade), tudo o que obterá é complexidade adicional e a necessidade de digitar muitas declarações globais que não são necessárias.

Seria melhor usar dados estruturados, como objetos ou injeção de dependência ou, no seu caso, um conjunto de funções.

Por exemplo, aqui está um meio de fazer algo semelhante por meio de variáveis ​​estáticas (ainda ruins pelas mesmas razões, mas um pouquinho menos e mais fáceis de digitar), por exemplo

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

Se você realmente deseja economizar tempo armazenando dados em algum lugar para reutilização, considere usar o WP_Cachesistema com wp_cache_getetc

Tom J Nowell
fonte
Eu sei que é um pouco maluco usar o escopo global, mas a maioria, se não todas, essas variáveis ​​serão usadas em todas as páginas. Estou aberto a melhores idéias. Vou editar a pergunta para tornar minha intenção um pouco mais clara. BTW funciona perfeitamente bem quando eu faço <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?>como por sua sugestão. Obrigado!
precisa
2
Ah, se minha solução funcionar, você poderia marcar como aceito? Suas variáveis ​​globais são tão rápidas quanto fazer a chamada original; você pode tentar usar funções para não precisar digitar duas linhas, melhor ainda, um singleton, melhor ainda, tornar tudo dinâmico e template parte incluída via get_template_part
Tom J Nowell
Marcado como aceito, é o que estou fazendo agora, embora possa seguir uma das estratégias que o MarkKaplun está sugerindo abaixo. Usar get_template_part () é uma ideia interessante, mas não tenho certeza se quero ter um diretório cheio de arquivos curtos como esse ... #
317
oooh não, não, você não gostaria de um arquivo para cada categoria, mas apenas aquele que pega o nome da categoria atual e o usa. Você não deveria ter que codificar nada, imagine o incômodo de codificar tudo
Tom J Nowell #
Coloquei o código no meu child-functions.php que está ativo. Mas não consigo acessar a variável em um arquivo php-include que chamo de uma postagem gerada por banco de dados "normal". Por favor, informe-me, o que faço de errado? (Eu defini-lo como global, é claro.)
ycc_swe
19

Não use variáveis ​​globais , tão simples quanto isso.

Por que não usar globais

Porque o uso de globais torna mais difícil a manutenção do software a longo prazo.

  • Um global pode ser declarado em qualquer lugar do código, ou em nenhum lugar; portanto, não há lugar em que você possa instintivamente procurar algum comentário sobre o uso do global.
  • Ao ler o código, você normalmente assume que as variáveis ​​são locais para a função e não entende que alterar seu valor em uma função pode ter uma alteração ampla no sistema.
  • Se eles não manipularem entrada, as funções deverão retornar o mesmo valor / saída quando forem chamadas com os mesmos parâmetros. O uso de globais em uma função introduz parâmetros adicionais que não estão documentados na declaração da função.
  • os globals não têm nenhuma construção de inicialização específica e, portanto, você nunca pode ter certeza de quando pode acessar o valor do global e não recebe nenhum erro ao tentar acessar o global antes da inicialização.
  • Outra pessoa (talvez um plugin) pode usar globais com o mesmo nome, arruinando seu código ou você estragando, dependendo da ordem de inicialização.

O núcleo do WordPress tem muito a ver com o uso de globais. Ao tentar entender como as funções básicas funcionam the_content, você repentinamente percebe que a $morevariável não é local, mas global e precisa pesquisar todos os arquivos principais para entender quando é configurada como verdadeira.

Então, o que pode ser feito ao tentar parar de copiar e colar várias linhas de código em vez de armazenar o resultado da primeira execução em um global? Existem várias abordagens, funcionais e OOP.

A função adoçante. É simplesmente um invólucro / macro para salvar a cópia / colar

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

Os benefícios são que agora existe uma documentação para o que o antigo global faz, e você tem um ponto óbvio para a depuração quando o valor retornado não é o esperado.

Depois de ter um adoçante, é fácil armazenar em cache o resultado, se necessário (faça-o apenas se você descobrir que essa função leva muito tempo para ser executada)

function notaglobal($id) {
  static $cache;

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

Isso fornece o mesmo comportamento de um global, mas com a vantagem de ter uma inicialização garantida toda vez que você o acessa.

Você pode ter padrões semelhantes com OOP. Acho que o OOP geralmente não agrega valor a plugins e temas, mas essa é uma discussão diferente

class notaglobal {
   var latestfoo2;

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

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

Este é um código desajeitado, mas se você tiver vários valores que gostaria de pré-calcular porque eles estão sempre sendo usados, esse pode ser um caminho a percorrer. Basicamente, este é um objeto que contém todos os seus globais de maneira organizada. Para evitar que uma instância desse objeto seja global (você deseja uma instância, caso contrário você recalcula os valores), convém usar um padrão singleton (algumas pessoas argumentam que é uma má idéia, YMMV)

Não gosto de acessar diretamente um atributo de objeto, portanto, no meu código, ele distorcerá um pouco mais

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
fonte
7
Por favor, não grite . Se importa em explicar o porquê e fornecer algum tipo de citação?
precisa saber é
Eu acho que você entendeu mal a resposta. Se ele não estivesse tentando fazer a otimização antecipada armazenando valores em variáveis ​​globais, seu código teria funcionado. O motivo é que seguir os princípios básicos estabelecidos para o desenvolvimento de software é algo que não pode ser enfatizado o suficiente. As pessoas que não entendem esse princípio básico (disponível no google local) não devem espalhar código pela rede.
Mark Kaplun
11
IMO, esta é uma resposta, as pessoas que vêm aqui do google devem ver que é uma má ideia pensar em usar globals imediatamente.
Mark Kaplun
6
Não basta dizer não fazer X, você tem que explicar o porquê ou parece que está dizendo isso por capricho
Tom J Nowell
11
@ TomJNowell, acho engraçado que eu tenha sido o único a rever a questão em si, pois estava obviamente fora do escopo do WASE. Não vi o valor de expandir um assunto que não deveria ter sido iniciado aqui.
Mark Kaplun
8

Sua pergunta está envolvida em como o php funciona.

Tome $ wpdb como exemplo

$ wpdb é uma variável global bem conhecida.

Você sabe quando será declarado e atribuído com valores?

Toda página carregada , sim, toda vez que você visita seu site wordpress.

Da mesma forma, você precisa garantir que as variáveis ​​que deseja globalizar serão declaradas e atribuídas com os valores correspondentes a cada página carregada.

Embora eu não seja um designer de temas, posso dizer que o after_setup_theme é um gancho de uma vez. só será acionado quando o tema for ativado.

Se eu fosse você, usarei init ou outros ganchos. Não, se eu fosse você, não usarei variáveis ​​globais ...

Eu realmente não sou bom em explicar as coisas. Portanto, você deve pegar um livro se quiser se aprofundar no PHP.

Jesse
fonte
2

Você sempre pode usar um padrão singleton através de getters 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
fonte