Pytanie Jak ustalić, czy ustawiona jest zmienna pass-by-reference?


Dla dowolnej funkcji zadeklarowanej w następujący sposób:

function foo($first, &$second = null) {
    // if ($second is assigned) {
    //     Work with $second
    // }
}

Jak określić, czy $second jest rzeczywiście przypisany do zmiennej w czasie połączenia, np .:

foo('hello', $second);

vs

foo('hello'); // Notice &$second is unassigned

Wypróbowany isset(), is_null() ale nie wydają się działać.

Aktualizacja:

Stworzył skrypt testowy tutaj tutaj


12
2017-10-28 16:26


pochodzenie


func_num_args () - Ja͢ck
Dzięki Jack. Nie myślałem o tym. To powinno działać. :) Każdy pomysł na bardziej konkretny, który dotyczy bezpośrednio $second? - uzyn
Osobiście po prostu użyłbym: if ($ second) {} - Ja͢ck
Opublikuj swoje rzeczywisty testcase, ponieważ oba isset i is_null "praca" dla mnie dobrze. - Lightness Races in Orbit
Właśnie dodałem tutaj testulec: codepad.org/HHaVoXrc Dziękuję za wszystkie świetne dyskusje na ten temat. - uzyn


Odpowiedzi:


Rozumiem, że chcesz zaimplementować funkcję, która implementuje coś takiego preg_match, przy czym trzeci argument zostanie zapełniony wzorami pamięci, jeśli zostanie przekazany.

Wewnętrznie, funkcje PHP używają funkcji o nazwie zend_parse_parameters(); akceptuje ciąg formatu i zmienną liczbę argumentów, które zostaną wypełnione meta danymi parametrów połączenia. Jeśli parametr nie jest przekazywany (np. Gdy jest opcjonalny), meta dane nie są dostępne i dlatego jest łatwy do wykrycia.

Wracając do samego PHP, niestety nie ma czegoś takiego jak func_arg_used($var) to ci powie, czy $var został przekazany jako argument funkcji; może to byłby interesujący wkład do języka, ale do tego czasu będziesz musiał zadowolić się czymś bardziej starożytnym :)

if (func_num_args() > 1) {
    // $second was passed and can be used to populate
}

Być może musisz zachować ostrożność podczas zmiany sygnatury funkcji, szczególnie gdy dodasz parametry przed $second; jednak naturalnie nie powinno się to często zdarzać, ponieważ zdecydowanie zepsułoby to zależne funkcje. Dodanie większej liczby argumentów na końcu nie ma wpływu na powyższy kod.

Można to zrobić na dwa sposoby:

  1. ReflectionFunction - nowa, lśniąca zabawka deweloperów, pozwala na introspekcję Twojej funkcji i ustalenie, czy sygnatura zmieniła się od kiedy została utworzona. Używaj go oszczędnie, ale introspekcja nie jest tania, szczególnie biorąc pod uwagę alternatywę.

  2. Skromny komentarz do kodu - znacznie zaniżona forma policyjnego kodowania; prosta linia, która mówi // IMPORTANT - don't add arguments before $second


3
2017-10-29 12:09



Dziękuję za bardzo dobrze wyjaśnioną odpowiedź, Jack. Rzeczywiście szukałem czegoś takiego preg_match(). - uzyn


Nie możesz (o ile mi wiadomo) odróżnić NULL od Undefined za pomocą isset. Możesz ustawić domyślną wartość $ sekundę na pewną losową wartość, która nigdy nie zostanie użyta inaczej.

define('UNDEFINED', -19181818);

function foo($first, &$second = UNDEFINED) 
{
 if($second==UNDEFINED)
 {
  etc..
 }
}

1
2017-10-28 17:24



nulljest już "wartością" (nie do końca), która nigdy nie będzie używana. Jeśli używasz go jako "wartości" innej niż "ta zmienna nie jest zdefiniowana", to robisz PHP źle. :) - Lightness Races in Orbit
Problem jest pusty, a niezdefiniowane są traktowane tak samo podczas testowania, jeśli zmienna została ustawiona, ale istnieje subtelna różnica w obu. W tym przypadku musisz przetestować na zmiennej, która nie jest równa null, chyba że istnieje inna funkcja niskiego poziomu w php nie jestem świadomy. Lub użyj func_num_args, który jest prawdopodobnie czystszym sposobem na zrobienie tego (chyba że pojawią się 3 i 4 arg itp.! - Niall
Ponieważ PHP traktuje je tak samo, więc powinniśmy. Nie rozumiem, dlaczego wszyscy chcą rozróżnić wysyłanie zmiennej ustawionej na null do funkcji. Jaka jest subtelna różnica? Barring łamie abstrakcję zdefiniowaną przez język i patrząc na szczegóły implementacji. - Lightness Races in Orbit
NULL to wartość, która może być użyta do określenia, czy zmienna nie zawiera danych. Może być przekazany i zwrócony przez funkcje. Ponadto PHP zgłosi błąd, jeśli spróbujesz uzyskać dostęp do niezdefiniowanej zmiennej. Jestem pewien, że istnieje kilka innych obszarów, w których PHP nie traktuje ich tak samo. Zgadzam się, nie rozumiem, dlaczego OP chce robić to, co robi, ale widzę kilka innych ważnych scenariuszy, w których warto wskazać różnicę między niezdefiniowanym a zerowym. - Niall
Nie widzę, że OP poprosił o dokonanie takiego rozróżnienia, ale ponieważ jesteś drugą osobą, która twierdzi inaczej, być może powinienem zakończyć ślepotę z mojej strony. - Lightness Races in Orbit


Pracuje dla mnie:

<?php

function bool_str($b)
{
    return ($b ? "true" : "false");
}

function foo($first, &$second = null)
{
    echo bool_str(isset($first)) . " " . bool_str(isset($second)) . " " .
         bool_str(is_null($first)) . " " . bool_str(is_null($second)) . "\n";
}

$a = 1;
$b = 2;

foo($a, $b);
foo($a);

?>

Wydajność:

true true false false
true false false true
     ^^^^^       ^^^^

Dokładnie tego oczekuję, tzn. Możesz użyć obu isset i is_null tutaj.


1
2017-10-28 18:10



to nie działa w przypadku $b = null; - goat
@rambocoder: Dzieje się tak dlatego, że nie ma rozróżnienia między zmiennymi ustawionymi na null i zmienna, która nie istnieje. Są jednym i tym samym. Właśnie dlatego warto coś ustawić null jest sposobem na uwolnienie tego. Próba wymuszenia rozróżnienia między nimi jest głupim posunięciem, ponieważ jest sprzeczne z architekturą PHP. Również PO nie poprosił o to. - Lightness Races in Orbit
Istnieje rozróżnienie. Nieistniejąca zmienna nie pojawi się w tabeli symboli. Na przykład przez get_defined_vars(). Bez względu na to, twoje rozwiązanie nie odpowiada na konkretne pytanie, które zostało zadane (chociaż jest to prawdopodobnie wystarczające i dobre rozwiązanie dla większości). - goat
@rambocoder: Tablica symboli nie ma znaczenia. Programowanie w PHP wymaga traktowania zmiennych ustawionych na null jako niewyraźne ze zmiennych, które nie zostały zadeklarowane. W związku z tym moje rozwiązanie jest w 100% niezawodne zgodnie z umowami tego języka. Przejście przez abstrakcje języka do szczegółów implementacji przechodzi w zupełnie inną historię. - Lightness Races in Orbit
Konkluzja: twoja odpowiedź kończy się niepowodzeniem w przypadku testu testowego, który podał. Podałem przykład. Koniec. - goat


Myślę, że sugestia Jacka do użycia func_num_args() jest doskonały.

function foo($first, &$second = null) {
    if (func_num_args() > 1) {
        //$second MUST be assigned
    }
}

To poprawnie wykrywa, kiedy $second odwołuje się do zmiennej, która istnieje w tabeli symboli zakresu wywołującego, ale ma wartość zmiennej o wartości null.

$second = null;
foo('hello', $second);

1
2017-10-28 18:30



Mogę to potwierdzić func_num_args() Prace. (Dzięki Jack!) Nie pomyślałem o tym, kiedy zadałem to pytanie, ale teraz zastanawiam się, czy jest taki, który zajmuje się zmienną pass-by-reference w przypadku zmiany sygnatury funkcji (i programista zapomina o aktualizacji func_num_args()). - uzyn