Dynamiczne tworzenie i wywoływanie funkcji to jeden z najbardziej elastycznych mechanizmów Perl, odziedziczony z tradycji lateksu i skryptów powłoki. Już od wczesnych wersji Perl pozwala na wywoływanie funkcji według ciągu (poprzez symboliczną referencję/globy), przechowywanie odniesień do podprogramów w zmiennych i tablicach asocjacyjnych, a także konstrukcję AUTOLOAD do generowania funkcji w locie.
Głównym problemem takiego podejścia jest bezpieczeństwo (możliwość wywołania niepożądanej funkcji według podstawionego ciągu) i wydajność (symboliczne rozwiązywanie nazw jest wolniejsze od bezpośredniego wywołania). Również ważna jest kontrola zakresu widoczności funkcji i przekazywanie odpowiedniej liczby argumentów.
Rozwiązanie — używać haszowego dispatcher’a (mapowanie z ciągu/klucza na coderef), unikać eval do uruchamiania kodu użytkownika, wyraźnie definiować listę dozwolonych funkcji do wywołania.
Przykład kodu (dystrybucja według klucza):
my %dispatch = ( add => sub { $_[0] + $_[1] }, sub => sub { $_[0] - $_[1] }, mult => sub { $_[0] * $_[1] }, ); my $key = 'add'; if (exists $dispatch{$key}) { print $dispatch{$key}->(2, 3); # Wyświetli 5 } else { die "Nieznana akcja $key"; }
Kluczowe cechy:
Czy można wywołać funkcję po jej nazwie, używając tylko ciągu?
Odpowiedź: Tak, ale to niebezpieczne — wywołanie $fn_name->() lub poprzez bezpośrednią symboliczną referencję &$fn_name(); nie jest zalecane z zewnętrznymi (użytkownikowymi) danymi, ponieważ prowadzi to do potencjalnych luk w bezpieczeństwie.
Czy istnieje różnica między referencją kodową a nazwą funkcji w Perl?
Odpowiedź: Tak, nazwa funkcji jest zawsze globalna, a referencja do funkcji (coderef) może być leksykalna, lokalna, przekazywana między podprogramami i przechowywać anonimową funkcję.
my $coderef = sub { ... }; my $named = \&fn_name;
Co się stanie, jeśli wywołasz nieistniejącą funkcję przez haszowego dispatcher’a?
Odpowiedź: Jeśli klucz nie istnieje — wystąpi błąd. Dlatego zawsze wymagana jest kontrola exists przed wywołaniem oraz obsługa nierozpoznanych komend, w przeciwnym razie wystąpi próba wywołania undef (błąd krytyczny).
Komenda na stronie pobiera nazwę funkcji z parametru GET i wywołuje przez eval — każdy użytkownik może wywołać system, unlink i inne niebezpieczne funkcje.
Zalety:
Wady:
Używany jest hasz z białą listą funkcji, wszystkie wskaźniki są walidowane, eval nie jest używane, błędy są przechwytywane i rejestrowane.
Zalety:
Wady: