Tentativa de fornecer uma visão geral das várias discussões e respostas:
Não existe uma resposta única para a pergunta que possa substituir todas as maneiras isset
possíveis. Alguns casos de uso são tratados por outras funções, enquanto outros não resistem ao escrutínio ou têm valor duvidoso além do código de golfe. Longe de ser "quebrado" ou "inconsistente", outros casos de uso demonstram por que isset
a reação danull
é ao comportamento lógico.
Casos de uso reais (com soluções)
1. Teclas de matriz
As matrizes podem ser tratadas como coleções de variáveis, com unset
e isset
tratando-as como se fossem. No entanto, como eles podem ser iterados, contados etc., um valor ausente não é o mesmo que aquele cujo valor énull
.
A resposta neste caso, é usar em array_key_exists()
vez deisset()
.
Como isso leva a matriz a verificar como argumento de função, o PHP ainda emitirá "avisos" se a própria matriz não existir. Em alguns casos, pode-se argumentar validamente que cada dimensão deveria ter sido inicializada primeiro, portanto o aviso está cumprindo sua função. Para outros casos, uma array_key_exists
função "recursiva" , que verificava cada dimensão da matriz por sua vez, evitaria isso, mas seria basicamente a mesma que @array_key_exists
. Também é um pouco tangencial para a manipulação de null
valores.
2. Propriedades do objeto
Na teoria tradicional da "Programação Orientada a Objetos", encapsulamento e polimorfismo são propriedades-chave dos objetos; em uma implementação OOP baseado em classes como PHP do, as propriedades encapsulados são declarados como parte da definição de classe, e dado os níveis de acesso ( public
, protected
, ou private
).
Entretanto, o PHP também permite adicionar propriedades dinamicamente a um objeto, como você usaria para uma matriz, e algumas pessoas usam objetos sem classe (tecnicamente, instâncias do built-in stdClass
, que não possui métodos ou funcionalidade privada) de maneira semelhante. maneira de matrizes associativas. Isso leva a situações em que uma função pode querer saber se uma propriedade específica foi adicionada ao objeto fornecido.
Assim como as chaves de matriz, uma solução para verificar as propriedades do objeto está incluída na linguagem, chamada razoavelmenteproperty_exists
.
Casos de uso não justificáveis, com discussão
3. register_globals
e outra poluição do espaço para nome global
O register_globals
recurso adicionou variáveis ao escopo global cujos nomes foram determinados por aspectos da solicitação HTTP (parâmetros GET e POST e cookies). Isso pode levar a códigos inseguros e com bugs, e é por isso que foi desativado por padrão desde o PHP 4.2, lançado em agosto de 2000 e removido completamente no PHP 5.4, lançado em março de 2012 . No entanto, é possível que alguns sistemas ainda estejam em execução com esse recurso ativado ou emulado. Também é possível "poluir" o espaço para nome global de outras maneiras, usando a global
palavra - chave ou $GLOBALS
matriz.
Em primeiro lugar, register_globals
é improvável que ela produza inesperadamente uma null
variável, pois os valores GET, POST e cookie sempre serão cadeias de caracteres ( ''
ainda retornando true
de isset
) e as variáveis na sessão devem estar inteiramente sob o controle do programador.
Em segundo lugar, a poluição de uma variável com o valor null
é apenas um problema se isso sobrescrever alguma inicialização anterior. "Sobrescrever" uma variável não inicializada null
apenas seria problemático se o código em outro lugar estivesse distinguindo entre os dois estados; portanto, essa possibilidade é um argumento contra essa distinção.
4. get_defined_vars
ecompact
Algumas funções raramente usadas no PHP, como get_defined_vars
e compact
, permitem tratar nomes de variáveis como se fossem chaves em uma matriz. Para variáveis globais, a matriz super global$GLOBALS
permite acesso semelhante e é mais comum. Esses métodos de acesso terão um comportamento diferente se uma variável não estiver definida no escopo relevante.
Depois de decidir tratar um conjunto de variáveis como uma matriz usando um desses mecanismos, você pode executar as mesmas operações que em qualquer matriz normal. Consequentemente, veja 1.
A funcionalidade que existia apenas para prever como essas funções estão prestes a se comportar (por exemplo, "haverá uma chave 'foo' na matriz retornada por get_defined_vars
?") É supérflua, pois você pode simplesmente executar a função e descobrir sem efeitos negativos.
4a Variáveis variáveis ( $$foo
)
Embora não sejam exatamente as funções que transformam um conjunto de variáveis em uma matriz associativa, a maioria dos casos usando "variáveis variáveis" ("atribuir a uma variável nomeada com base nessa outra variável") pode e deve ser alterada para usar uma matriz associativa .
Um nome de variável, fundamentalmente, é o rótulo dado a um valor pelo programador; se você está determinando em tempo de execução, não é realmente um rótulo, mas uma chave em algum armazenamento de valor-chave. Mais praticamente, ao não usar uma matriz, você está perdendo a capacidade de contar, repetir, etc; também pode ser impossível ter uma variável "fora" do armazenamento de valores-chave, pois ela pode ser substituída por $$foo
.
Uma vez alterado para usar uma matriz associativa, o código será passível de solução 1. O acesso indireto à propriedade do objeto (por exemplo $foo->$property_name
) pode ser tratado com a solução 2.
5. isset
é muito mais fácil digitar do quearray_key_exists
Não tenho certeza se isso é realmente relevante, mas sim, os nomes das funções do PHP podem ser bastante demorados e inconsistentes às vezes. Aparentemente, as versões pré-históricas do PHP usavam o comprimento de um nome de função como uma chave de hash, então Rasmus inventou deliberadamente nomes de funções como htmlspecialchars
para que eles tivessem um número incomum de caracteres ...
Ainda assim, pelo menos não estamos escrevendo Java, não é? ;)
6. Variáveis não inicializadas têm um tipo
A página de manual sobre conceitos básicos de variável inclui esta declaração:
Variáveis não inicializadas têm um valor padrão de seu tipo, dependendo do contexto em que são usadas
Não tenho certeza se existe alguma noção no Zend Engine de "tipo não inicializado mas conhecido" ou se isso está lendo muito na declaração.
O que está claro é que não faz diferença prática para o comportamento deles, pois os comportamentos descritos nessa página para variáveis não inicializadas são idênticos ao comportamento de uma variável cujo valor é null
. Para escolher um exemplo, ambos $a
e $b
neste código terminarão como o número inteiro 42
:
unset($a);
$a += 42;
$b = null;
$b += 42;
(O primeiro alertará sobre uma variável não declarada, na tentativa de fazer com que você escreva um código melhor, mas não fará diferença na maneira como o código realmente é executado.)
99. Detectando se uma função foi executada
(Mantendo este último, pois é muito mais longo que os outros. Talvez eu o edite mais tarde ...)
Considere o seguinte código:
$test_value = 'hello';
foreach ( $list_of_things as $thing ) {
if ( some_test($thing, $test_value) ) {
$result = some_function($thing);
}
}
if ( isset($result) ) {
echo 'The test passed at least once!';
}
Se some_function
puder retornar null
, existe a possibilidade de que o echo
alcance não seja alcançado mesmo que some_test
retornado true
. A intenção do programador era detectar quando $result
nunca havia sido definido, mas o PHP não permite que eles o façam.
No entanto, existem outros problemas com essa abordagem, que ficam claros se você adicionar um loop externo:
foreach ( $list_of_tests as $test_value ) {
// something's missing here...
foreach ( $list_of_things as $thing ) {
if ( some_test($thing, $test_value) ) {
$result = some_function($thing);
}
}
if ( isset($result) ) {
echo 'The test passed at least once!';
}
}
Como $result
nunca é inicializado explicitamente, ele assumirá um valor quando o primeiro teste for aprovado, tornando impossível saber se os testes subsequentes foram aprovados ou não. Este é realmente um erro extremamente comum quando as variáveis não são inicializadas corretamente.
Para corrigir isso, precisamos fazer algo na linha em que comentei que algo está faltando. A solução mais óbvia é definir $result
um "valor terminal" que some_function
nunca pode retornar; se for esse o caso null
, o restante do código funcionará bem. Se não houver candidato natural para um valor terminal, pois some_function
possui um tipo de retorno extremamente imprevisível (que provavelmente é um sinal ruim), então um valor booleano adicional, por exemplo $found
, poderia ser usado.
Experimento de pensamento um: a very_null
constante
Teoricamente, o PHP poderia fornecer uma constante especial - assim como null
- para ser usada como um valor terminal aqui; presumivelmente, seria ilegal retornar isso de uma função, ou seria coagido a isso null
, e provavelmente o mesmo se aplicaria a passá-lo como argumento de função. Isso tornaria esse caso muito específico um pouco mais simples, mas assim que você decidisse redefinir o código - por exemplo, para colocar o loop interno em uma função separada - ele se tornaria inútil. Se a constante pudesse ser transmitida entre funções, não some_function
seria possível garantir que não a retornaria, de modo que não seria mais útil como um valor terminal universal.
O argumento para detectar variáveis não inicializadas, neste caso, se resume ao argumento para essa constante especial: se você substituir o comentário por unset($result)
e tratá-lo de maneira diferente $result = null
, estará apresentando um "valor" para o $result
qual não pode ser repassado e só pode ser detectado por funções internas específicas.
Experimento de pensamento dois: contador de tarefas
Outra maneira de pensar sobre o que a última if
pergunta é é "alguma coisa foi feita $result
?" Em vez de considerá-lo um valor especial $result
, você pode pensar nisso como "metadados" sobre a variável, um pouco como a "contaminação de variáveis" do Perl. Então ao invés de isset
que você pode chamá-lo has_been_assigned_to
, e ao invés de unset
, reset_assignment_state
.
Mas se sim, por que parar em um booleano? E se você quiser saber quantas vezes o teste passou; você pode simplesmente estender seus metadados para um número inteiro e ter get_assignment_count
e reset_assignment_count
...
Obviamente, a adição de um recurso desse tipo teria uma compensação na complexidade e no desempenho do idioma; portanto, ele precisaria ser pesado com relação à sua utilidade esperada. Como com uma very_null
constante, seria útil apenas em circunstâncias muito estreitas e seria similarmente resistente à re-fatoração.
A pergunta esperançosamente óbvia é por que o mecanismo de tempo de execução PHP deve assumir antecipadamente que você deseja acompanhar essas coisas, em vez de deixá-lo explicitamente, usando o código normal.