PHP global em funções

100

Qual é a utilidade da palavra-chave global ?

Existem razões para preferir um método a outro?

  • Segurança?
  • Atuação?
  • Algo mais?

Método 1:

function exempleConcat($str1, $str2)
{
  return $str1.$str2;
}

Método 2:

function exempleConcat()
{
  global $str1, $str2;
  return $str1.$str2;
}

Quando faz sentido usar global?

Para mim, parece ser perigoso ... mas pode ser apenas uma falta de conhecimento. Estou interessado em razões técnicas documentadas (por exemplo, com exemplo de código, link para documentação ...).

Desde já, obrigado!


Recompensa

Esta é uma boa pergunta geral sobre o assunto, eu (@Gordon) estou oferecendo uma recompensa para obter respostas adicionais. Não importa se sua resposta está de acordo com a minha ou fornece um ponto de vista diferente. Como o globaltópico surge de vez em quando, poderíamos usar uma boa resposta "canônica" para criar um link.

Pascal Qyy
fonte
2
dê uma olhada neste link: stackoverflow.com/questions/1557787 Há muitos artigos relacionados no canto inferior direito desta página
JohnP
Não é uma resposta direta à sua pergunta, mas por favor leia esta pergunta do SO mais antiga .
Ólafur Waage
1
Portanto, não consigo ler nenhuma palavra-chave pró-global. 1) Por que está aqui. 2) Por que as pessoas usam?
Pascal Qyy
@ G.Qyy Por que existe goto? Por que as pessoas usam isso? Eles não usam (espero pelo menos): P
PeeHaa
No final do ano passado (14 de dezembro), alguém votou contra esta questão. Estou muito interessado em saber por quê, porque todos os pontos de vista, inclusive os negativos, são interessantes. Neste caso, mais do que nunca! Ficarei muito grato por qualquer pista sobre isso.
Pascal Qyy

Respostas:

158

Globais são maus

Isso é verdadeiro para a globalpalavra - chave e também para tudo o mais que vai do escopo local ao global (estática, singletons, registros, constantes). Você não quer usá-los. Uma chamada de função não deve depender de nada externo, por exemplo

function fn()
{
    global $foo;              // never ever use that
    $a = SOME_CONSTANT        // do not use that
    $b = Foo::SOME_CONSTANT;  // do not use that unless self::
    $c = $GLOBALS['foo'];     // incl. any other superglobal ($_GET, …)
    $d = Foo::bar();          // any static call, incl. Singletons and Registries
}

Tudo isso fará com que seu código dependa de fora. O que significa que você precisa saber o estado global completo do seu aplicativo antes de chamar qualquer um deles de forma confiável. A função não pode existir sem esse ambiente.

Usar as superglobais pode não ser uma falha óbvia, mas se você chamar seu código de uma linha de comando, não terá $_GETou $_POST. Se o seu código depende da entrada deles, você está se limitando a um ambiente da web. Basta abstrair a solicitação em um objeto e usá-lo em seu lugar.

No caso de combinação de nomes de classe codificados (estáticos, constantes), sua função também não pode existir sem que essa classe esteja disponível. Isso é menos problemático quando se trata de classes do mesmo namespace, mas quando você começa a mesclar a partir de diferentes namespaces, está criando uma confusão emaranhada.

A reutilização é severamente prejudicada por todos os itens acima. O teste de unidade também .

Além disso, suas assinaturas de função estão mentindo quando você acopla ao escopo global

function fn()

é um mentiroso, porque afirma que posso chamar essa função sem passar nada para ela. É apenas quando olho para o corpo funcional que aprendo que tenho que definir o ambiente em um determinado estado.

Se a sua função requer argumentos para ser executada, torne-os explícitos e passe-os em:

function fn($arg1, $arg2)
{
    // do sth with $arguments
}

transmite claramente a partir da assinatura o que ela precisa para ser chamada. Não depende do ambiente estar em um estado específico. Você não tem que fazer

$arg1 = 'foo';
$arg2 = 'bar';
fn();

É uma questão de puxar (palavra-chave global) em vez de inserir (argumentos). Quando você empurra / injeta dependências, a função não depende mais do exterior. Quando você faz isso, fn(1)você não precisa ter uma variável segurando 1 em algum lugar externo. Mas quando você puxa global $onedentro da função, você acopla o escopo global e espera que ele tenha uma variável definida em algum lugar. A função não é mais independente então.

Pior ainda, quando você muda os globais dentro de sua função, seu código rapidamente se torna completamente incompreensível, porque suas funções estão tendo efeitos colaterais em todo o lugar.

Na falta de um exemplo melhor, considere

function fn()
{
    global $foo;
    echo $foo;     // side effect: echo'ing
    $foo = 'bar';  // side effect: changing
}

E então você faz

$foo = 'foo';
fn(); // prints foo
fn(); // prints bar <-- WTF!!

Não há como ver que $foomudou nessas três linhas. Por que chamar a mesma função com os mesmos argumentos mudaria repentinamente sua saída ou mudaria um valor no estado global? Uma função deve fazer X para uma entrada Y definida. Sempre.

Isso se torna ainda mais grave ao usar OOP, porque OOP é sobre encapsulamento e, ao alcançar o escopo global, você está interrompendo o encapsulamento. Todos esses singletons e registros que você vê nas estruturas são odores de código que devem ser removidos em favor da injeção de dependência. Separe seu código.

Mais recursos:

Gordon
fonte
10
Por que o PHP implementa essas coisas? Existe um utilitário? Sempre me surpreendo com implementações perigosas em PHP que muitas pessoas usam o tempo todo ... É difícil acreditar que não haja razões lógicas!
Pascal Qyy
5
Eu gostaria que você pudesse fazer Globals are evil maiores.
Kermit
3
Uau, finalmente alguém explicou bem porque os globais são maus ... Eu sempre ouvi que eles eram e vi alguns exemplos muito específicos do porquê, mas esta é realmente uma explicação boa e abrangente para o motivo geral. +1
Wingblade
Estou muito atrasado e meio que entendo o que você está dizendo, mas e as conexões mysqli? Devem ser passados ​​como um parâmetro a cada vez, ou é um link $ global; permitido em seus olhos?
Mave
2
Você está certo, exceto pelas constantes. Eles não representam um "estado" do aplicativo e não há problema em se referir a eles em uma função. A função não "mente" se estiver usando uma constante interna. Concordo que isso implica que o programador em um ponto tinha conhecimento de fora, mas essa é uma troca muito aceitável pelo que é uma constante. Além disso, sério, não é um grande problema.
Sebas
35

O único grande motivo contra globalé que isso significa que a função depende de outro escopo. Isso vai ficar confuso muito rapidamente.

$str1 = 'foo';
$str2 = 'bar';
$str3 = exampleConcat();

vs.

$str = exampleConcat('foo', 'bar');

Exigir $str1e $str2ser configurado no escopo de chamada para que a função funcione significa que você introduz dependências desnecessárias. Você não pode mais renomear essas variáveis ​​neste escopo sem renomeá-las na função também e, portanto, também em todos os outros escopos que você está usando esta função. Isso logo se transforma em caos, pois você está tentando controlar os nomes das variáveis.

globalé um padrão ruim até mesmo para incluir coisas globais, como $dbrecursos. Não vai chegar o dia em que pretende mudar o nome $db, mas não pode, porque a sua aplicação inteira depende do nome.

Limitar e separar o escopo das variáveis ​​é essencial para escrever qualquer aplicativo meio complexo.

deceze
fonte
1
Sinto muito, mas por que eu definitivamente desejaria renomear $db? É o PDO, espalhado por toda parte. Por que seria alterado quando posso atualizar as informações de conexão separadamente?
Casey Dwayne de
3
@kcd Porque um dia você percebeu o quão grande é a injeção de dependência e quer reestruturar seu aplicativo? Porque um dia você vai precisar integrar seu material com algum outro material que também usa uma $dbvariável global ? Porque um dia você descobrirá o teste de unidade e precisará gerenciar mais de uma conexão de banco de dados por vez para isso? Muitas, muitas razões.
deceze
35

Globais são inevitáveis.

É uma discussão antiga, mas ainda gostaria de acrescentar algumas reflexões porque sinto falta delas nas respostas mencionadas acima. Essas respostas simplificam demais o que é um global e apresentam soluções que não são soluções para o problema. O problema é: qual a maneira adequada de lidar com uma variável global e o uso da palavra-chave global? Para isso, primeiro temos que examinar e descrever o que é um global.

Dê uma olhada neste código do Zend - e por favor, entenda que eu não sugiro que o Zend seja mal escrito:

class DecoratorPluginManager extends AbstractPluginManager
{
/**
 * Default set of decorators
 *
 * @var array
 */
protected $invokableClasses = array(
    'htmlcloud' => 'Zend\Tag\Cloud\Decorator\HtmlCloud',
    'htmltag'   => 'Zend\Tag\Cloud\Decorator\HtmlTag',
    'tag'       => 'Zend\Tag\Cloud\Decorator\HtmlTag',
   );

Existem muitas dependências invisíveis aqui. Essas constantes são, na verdade, classes. Você também pode ver require_once em algumas páginas desta estrutura. Require_once é uma dependência global, portanto, criando dependências externas. Isso é inevitável para uma estrutura. Como você pode criar uma classe como DecoratorPluginManager sem muito código externo do qual depende? Não pode funcionar sem muitos extras. Usando o framework Zend, você já mudou a implementação de uma interface? Uma interface é, na verdade, global.

Outro aplicativo usado globalmente é o Drupal. Eles estão muito preocupados com o design adequado, mas, assim como qualquer grande framework, eles têm muitas dependências externas. Dê uma olhada nos globais nesta página:

/**
 * @file
 * Initiates a browser-based installation of Drupal.
 */

/**
 * Root directory of Drupal installation.
 */
define('DRUPAL_ROOT', getcwd());

/**
 * Global flag to indicate that site is in installation mode.
 */
define('MAINTENANCE_MODE', 'install');

// Exit early if running an incompatible PHP version to avoid fatal errors.
if (version_compare(PHP_VERSION, '5.2.4') < 0) {
  print 'Your PHP installation is too old. Drupal requires at least PHP 5.2.4. See the     <a     href="http://drupal.org/requirements">system requirements</a> page for more     information.';
  exit;
}

// Start the installer.
require_once DRUPAL_ROOT . '/includes/install.core.inc';
install_drupal();

Já escreveu um redirecionamento para a página de login? Isso está mudando um valor global. (E então você não está dizendo 'WTF', que considero uma boa reação à má documentação do seu aplicativo.) O problema com os globais não é que sejam globais, você precisa deles para ter um aplicativo significativo. O problema é a complexidade do aplicativo geral, que pode torná-lo um pesadelo de lidar. As sessões são globais, $ _POST é global, DRUPAL_ROOT é global, o includes / install.core.inc 'é um global não modificável. Existe um grande mundo fora de qualquer função necessária para permitir que essa função faça seu trabalho.

A resposta de Gordon está incorreta, porque ele superestima a independência de uma função e chamar uma função de mentirosa é simplificar demais a situação. Funções não mentem e quando você dá uma olhada em seu exemplo, a função é projetada incorretamente - seu exemplo é um bug. (A propósito, concordo com a conclusão de que devemos desacoplar o código.) A resposta do deceze não é realmente uma definição adequada da situação. As funções sempre funcionam dentro de um escopo mais amplo e seu exemplo é muito simplista. Todos concordaremos com ele que essa função é totalmente inútil, porque retorna uma constante. Essa função é, de qualquer forma, um design ruim. Se você quiser mostrar que a prática é ruim, venha com um exemplo relevante. Renomear variáveis ​​em um aplicativo não é grande coisa ter um bom IDE (ou uma ferramenta). A questão é sobre o escopo da variável, não a diferença de escopo com a função. Existe um momento adequado para uma função desempenhar seu papel no processo (é por isso que ela é criada em primeiro lugar) e nesse momento adequado pode influenciar o funcionamento da aplicação como um todo, portanto, também trabalhando em variáveis ​​globais . A resposta de xzyfer é uma declaração sem argumentação. Os globais estão presentes em um aplicativo se você tiver funções procedurais ou design OOP. As próximas duas maneiras de alterar o valor de um global são essencialmente as mesmas: portanto, também trabalhando em variáveis ​​globais. A resposta de xzyfer é uma declaração sem argumentação. Os globais estão presentes em um aplicativo se você tiver funções procedurais ou design OOP. As próximas duas maneiras de alterar o valor de um global são essencialmente as mesmas: portanto, também trabalhando em variáveis ​​globais. A resposta de xzyfer é uma declaração sem argumentação. Os globais estão presentes em um aplicativo se você tiver funções procedurais ou design OOP. As próximas duas maneiras de alterar o valor de um global são essencialmente as mesmas:

function xzy($var){
 global $z;
 $z = $var;
}

function setZ($var){
 $this->z = $var;
}

Em ambos os casos, o valor de $ z é alterado em uma função específica. Em ambas as formas de programação, você pode fazer essas alterações em vários outros locais do código. Você poderia dizer que usando global você poderia chamar $ z em qualquer lugar e mudar lá. Sim você pode. Mas você vai? E quando feito em locais inadequados, não deveria ser chamado de bug?

Bob Fanger comenta sobre xzyfer.

Alguém deveria usar qualquer coisa, especialmente a palavra-chave 'global'? Não, mas como qualquer tipo de projeto, tente analisar o que depende e o que depende disso. Tente descobrir quando ele muda e como ele muda. A alteração dos valores globais só deve acontecer com as variáveis ​​que podem ser alteradas a cada solicitação / resposta. Ou seja, apenas para aquelas variáveis ​​que pertencem ao fluxo funcional de um processo, não à sua implementação técnica. O redirecionamento de uma URL para a página de login pertence ao fluxo funcional de um processo, a classe de implementação usada para uma interface para a implementação técnica. Você pode alterar o último durante as diferentes versões do aplicativo, mas não deve alterá-los a cada solicitação / resposta.

Para entender melhor quando é um problema trabalhar com globais e a palavra-chave global e quando não, vou apresentar a próxima frase, que vem de Wim de Bie ao escrever sobre blogs: 'Pessoal sim, privado não'. Quando uma função está mudando o valor de uma variável global para seu próprio funcionamento, chamarei esse uso privado de uma variável global e um bug. Mas quando a alteração da variável global é feita para o processamento adequado da aplicação como um todo, como o redirecionamento do usuário para a página de login, então isso na minha opinião é possivelmente um bom design, não por definição ruim e certamente não um anti-padrão.

Em retrospecto, as respostas de Gordon, deceze e xzyfer: todos eles têm 'sim privado' (e bugs) como exemplos. É por isso que eles se opõem ao uso de globais. Eu faria também Eles, no entanto, não vêm com exemplos de 'sim pessoal, não particular' como fiz nesta resposta várias vezes.

Loek Bergman
fonte
O exemplo de código Drupal não usa globais, ele usa constantes. Uma diferença muito importante é que uma constante não pode ser redefinida depois de definida. Além disso, você não pode simplesmente comparar as funções xyze setZ. O primeiro altera o estado global, o segundo é um método de classe e apenas altera o estado da instância em que foi chamado.
Arjan
@Arjen: se você pesquisar a palavra-chave global no Drupal 7.14, obterá centenas de resultados. É um problema antigo com setters públicos: você não controla o lugar onde eles são alterados depois de torná-los públicos. Foi aconselhado não usá-los ou declará-los privados, portanto, não podem ser adicionados posteriormente.
Loek Bergman
@Arjan: devido ao meu erro com a grafia do seu nome, você não recebeu nenhuma notificação da minha resposta para você. Agora você vai. :-)
Loek Bergman
@LoekBergman: Existem cerca de 400 ocorrências para a palavra globalno drupal 7.26 (que é a versão mais recente), algumas dessas ocorrências estão em comentários e várias outras parecem estar em um código que não é alterado há anos. Espero que eles não usem globals no drupal 8.
Arjan
@LoekBergman Use setters e getters. Não leva muito tempo para configurar e permite que outras pessoas que estão usando seu código e talvez estendendo suas classes tenham mais controle. Depois de tornar um parâmetro público, é isso. você não tem a opção de ocultá-lo mais tarde.
mAsT3RpEE
15

Simplificando, raramente há uma razão globale nunca uma boa no código PHP moderno IMHO. Especialmente se você estiver usando PHP 5. E ainda mais se você desenvolver código Orientado a Objetos.

Globais afetam negativamente a manutenibilidade, legibilidade e testabilidade do código. Muitos usos de globalpodem e devem ser substituídos por injeção de dependência ou simplesmente passando o objeto global como um parâmetro.

function getCustomer($db, $id) {
    $row = $db->fetchRow('SELECT * FROM customer WHERE id = '.$db->quote($id));
    return $row;
}
xzyfer
fonte
10

Não hesite em usar palavras-chave globais dentro de funções em PHP. Especialmente não aceite pessoas que estão bizarramente pregando / gritando como os globais são "maus" e outros enfeites.

Em primeiro lugar, porque o que você usa depende totalmente da situação e do problema, e NÃO existe uma solução / maneira de fazer nada na codificação. Deixando totalmente de lado a falácia de adjetivos religiosos subjetivos e indefiníveis como "mal" na equação.

Caso em questão :

Wordpress e seu ecossistema usam palavras-chave globais em suas funções. Seja o código OOP ou não OOP.

E a partir de agora o Wordpress é basicamente 18,9% da internet, e está rodando os enormes megasites / apps de inúmeros gigantes, desde Reuters a Sony, NYT e CNN.

E funciona bem.

O uso de palavras-chave globais dentro de funções libera o Wordpress do inchaço MASSIVO que aconteceria devido ao seu enorme ecossistema. Imagine que cada função solicite / passe qualquer variável necessária de outro plugin, núcleo e retorne. Adicionado com interdependências de plug-ins, que terminariam em um pesadelo de variáveis ​​ou um pesadelo de matrizes passadas como variáveis. Um inferno para rastrear, um inferno para depurar, um inferno para desenvolver. Pegada de memória absurdamente massiva devido ao inchaço de código e de variável também. Mais difícil de escrever também.

Pode haver gente que venha criticar o Wordpress, seu ecossistema, suas práticas e o que se passa por ali.

Inútil, já que esse ecossistema é praticamente 20% de quase toda a Internet. Aparentemente, FUNCIONA, faz seu trabalho e muito mais. O que significa o mesmo para a palavra-chave global.

Outro bom exemplo é o fundamentalismo "iframes são maus". Uma década atrás, era uma heresia usar iframes. E havia milhares de pessoas pregando contra eles na internet. Em seguida, vem o Facebook, depois vem o social, agora os iframes estão em toda parte, de caixas de 'curtir' a autenticação e voila - calem a boca. Existem aqueles que ainda não se calaram - com ou sem razão. Mas você sabe o quê, a vida continua, apesar dessas opiniões, e até mesmo aqueles que pregavam contra os iframes há uma década agora estão tendo que usá-los para integrar vários aplicativos sociais aos aplicativos de suas próprias organizações sem dizer uma palavra.

......

Fundamentalismo Coder é algo muito, muito ruim. Uma pequena porcentagem entre nós pode ser agraciada com o trabalho confortável em uma empresa sólida e monolítica que tem influência suficiente para suportar as constantes mudanças na tecnologia da informação e as pressões que ela traz em relação à competição, tempo, orçamento e outras considerações e, portanto, pode praticar fundamentalismo e adesão estrita aos "males" ou "bens" percebidos. Posições confortáveis ​​que lembram a velhice, mesmo se os ocupantes forem jovens.

Para a maioria, entretanto, o mundo das TI é um mundo em constante mudança, no qual eles precisam ter a mente aberta e ser práticos. Não há lugar para o fundamentalismo, deixe de lado palavras-chave ultrajantes como "mal" nas trincheiras da linha de frente da tecnologia da informação.

Basta usar o que for mais adequado para o problema À MÃO, com as devidas considerações para um futuro próximo, médio e longo. Não hesite em usar qualquer recurso ou abordagem porque existe uma animosidade ideológica galopante contra ele, entre qualquer subconjunto de codificadores.

Eles não farão o seu trabalho. Você irá. Aja de acordo com suas circunstâncias.

unidade100
fonte
3
+1 para o anti-fundamentalismo e assim, mas dizer apenas "muitas pessoas usam / funcionam / etc" é apenas um "argumentum ad populum", um sofisma básico. o fato de a maioria das pessoas pensar ou fazer uma coisa não prova que estão certas! em uma multidão, se surgir um perigo, a maioria das pessoas fará coisas estúpidas e algumas pessoas morrerão pisoteadas por outras. Eles estão certos em colocar o pé no rosto desta menina de cinco anos só porque eles acham que devem absolutamente empurrar uma porta que só se abre se for puxada para escapar do fogo? Acho que não…
Pascal Qyy
1
a maioria fazendo algo, é claro, não valida nada por si só. entretanto, o caso é o software. e se a maioria está fazendo isso, e a maioria dos aplicativos e serviços criados por essas pessoas estão funcionando bem (wordpress para muitos outros), isso significa que eles podem ser usados.
Unit100 02 de
7

Acho que todo mundo expôs bastante sobre os aspectos negativos dos globais. Portanto, adicionarei os aspectos positivos, bem como as instruções para o uso adequado de globais:

  1. O principal objetivo dos globais era compartilhar informações entre funções. de volta quando não havia nada como uma classe, o código php consistia em um monte de funções. Às vezes, você precisa compartilhar informações entre as funções. Normalmente, o global foi usado para fazer isso com o risco de ter os dados corrompidos ao torná-los globais.

    Agora, antes de algum simplório feliz e feliz começar um comentário sobre injeção de dependência, eu gostaria de perguntar a você como o usuário de uma função como exemplo get_post(1)saberia todas as dependências da função. Considere também que as dependências podem diferir de
    versão para versão e de servidor para servidor. O principal problema com a injeção de dependência é que as dependências precisam ser conhecidas de antemão. Em uma situação em que isso não seja possível ou variáveis ​​globais indesejadas fossem a única maneira de atingir esse objetivo.

    Devido à criação da classe, agora as funções comuns podem ser facilmente agrupadas em uma classe e compartilhar dados. Por meio de implementações como Mediadores, até objetos não relacionados podem compartilhar informações. Isso não é mais necessário.

  2. Outro uso para globais é para fins de configuração. Principalmente no início de um script, antes que os carregadores automáticos sejam carregados, as conexões de banco de dados sejam feitas, etc.

    Durante o carregamento de recursos, os globais podem ser usados ​​para configurar dados (ou seja, qual banco de dados usar, onde os arquivos da biblioteca estão localizados, a url do servidor, etc.). A melhor maneira de fazer isso é usando a define()função, pois esses valores não mudam com freqüência e podem ser facilmente colocados em um arquivo de configuração.

  3. O uso final para globais é manter dados comuns (ou seja, CRLF, IMAGE_DIR, IMAGE_DIR_URL), sinalizadores de status legíveis por humanos (ou seja, ITERATOR_IS_RECURSIVE). Aqui, os globais são usados ​​para armazenar informações que devem ser usadas em todo o aplicativo, permitindo que sejam alteradas e tenham essas alterações exibidas em todo o aplicativo.

  4. O padrão singleton se tornou popular no php durante o php4, quando cada instância de um objeto ocupava memória. O singleton ajudou a salvar ram, permitindo apenas a criação de uma instância de um objeto. Antes das referências, mesmo a injeção de dependência teria sido uma má ideia.

    A nova implementação de php de objetos do PHP 5.4+ cuida da maioria desses problemas, você pode passar objetos com segurança com pouca ou nenhuma penalidade mais. Isso não é mais necessário.

    Outro uso para singletons é a instância especial em que apenas uma instância de um objeto deve existir por vez, essa instância pode existir antes / depois da execução do script e esse objeto é compartilhado entre diferentes scripts / servidores / linguagens etc. Aqui, um padrão singleton resolve o solução muito bem.

Concluindo, se você está na posição 1, 2 ou 3, usar um global seria razoável. No entanto, em outras situações, o Método 1 deve ser usado.

Sinta-se à vontade para atualizar quaisquer outras instâncias em que globais devam ser usados.

mAsT3RpEE
fonte
6

Não faz sentido criar uma função concat usando a palavra-chave global.

É usado para acessar variáveis ​​globais, como um objeto de banco de dados.

Exemplo:

function getCustomer($id) {
  global $db;
  $row = $db->fetchRow('SELECT * FROM customer WHERE id = '.$db->quote($id));
  return $row;
}

Ele pode ser usado como uma variação do padrão Singleton

Bob Fanger
fonte
"não faz sentido" - na verdade faz: um exemplo será a implementação de uma tabela de pesquisa sem usar OOP.
Nir Alfasi