2011-08-08 11 views
7

Przed przejściem ciąg do eval() Chciałbym się upewnić, że składnia jest poprawna i umożliwiają:Waliduj użytkownik wpisał kod PHP przed przekazaniem go do eval()

  1. dwie funkcje: a() ib()
  2. czterech operatorów:/* - +
  3. Wsporniki()
  4. liczbach: 1,2, -1, 1

Jak mogę to zrobić, może to ma coś wspólnego z PHP Tokenizer?

Właściwie próbuję stworzyć prosty interpreter formuł, więc a() i b() zostaną zastąpione przez ln() i exp(). Nie chcę pisać tokenizera i parsera od zera.

+0

Czy interesuje Cię kolejność możliwych wejść? – hoppa

+0

Należy podać przykład funkcji, którą można dopuścić, a funkcja, która nie powinna przejść, powinna zostać dodana do pytania. Uwaga: nie należy lekceważyć korzystania z eval. –

+2

To dlatego chce najpierw zdezynfekować podejrzewam;) – hoppa

Odpowiedz

2

generatory parsera rzeczywiście już napisany dla PHP i „wapno” w szczególności pochodzi z typowej „kalkulatora” przykład, co byłoby oczywistym punktem wyjścia dla „mini języku”: http://sourceforge.net/projects/lime-php/

minęło lata odkąd ostatnio grałem w LIME, ale było już wtedy stabilne &.

Uwagi:

1) Korzystanie w pełni na parser generator daje przewagę unikania eval() w PHP w całości, jeśli chcesz - możesz LIME emitują parser który skutecznie zapewnia funkcję „eval” dla wyrażenia napisane w twoim mini języku (z zapieczętowanym sprawdzeniem poprawności). Daje to dodatkową korzyść, umożliwiając w razie potrzeby dodanie obsługi nowych funkcji.

2) Początkowo może się wydawać, że za dużo użyć generatora analizatora składni dla tak pozornie małego zadania, ale gdy tylko uzyskasz działające przykłady, będziesz pod wrażeniem tego, jak łatwo można je modyfikować i rozszerzać. I bardzo trudno jest zaniżać trudność napisania parsera bez błędów (nawet "trywialnego") od zera.

+0

@stereofrog - Przepraszamy, błędnie przeczytałem historię zmian. Link powinien teraz zadziałać, chociaż wiem, że nie pasuje do preferowanego formatu (z jakiegoś powodu odsyłacze w nawiasie nie są dla mnie poprawnie wyświetlane w Chrome). – Peter

0

Tak, potrzebujesz Tokenizera lub czegoś podobnego, ale to tylko część historii. Tokenizer (zwany potocznie "lexer") może tylko czytać i analizować elementy wyrażenia, ale nie ma możliwości wykrycia, że ​​coś takiego jak "foo() + * bar)" jest nieprawidłowe. Potrzebujesz drugiej części o nazwie parser, która mogłaby ustawić żetony w rodzaju drzewa (zwanego "AST") lub dostarczyć komunikat o błędzie, gdy tego nie zrobi. Jak na ironię, kiedy już masz drzewo, "eval" już nie jest potrzebne, możesz ocenić swoją ekspresję bezpośrednio z drzewa.

Polecam pisać ręcznie parser, ponieważ jest to bardzo przydatne ćwiczenie i dużo zabawy. Recursive descent parsers są dość łatwe do zaprogramowania.

+0

Zgadzam się, że pisanie parsera ręcznie jest zabawne i jest częścią podstawowego szkolenia dla każdego poważnego programisty. Ale jeśli pracujesz "na zegarze", lepiej byłoby, gdyby IMO ponownie wykorzystał generator wychodzący z poprzedniej wersji i poświęcił trochę czasu na zapisywanie nauki (i zabawy z) definicjami gramatyki. – Peter

+0

Rozumiem twój punkt, ale nie zgadzam się w przypadku parserów w szczególności. Po pierwsze, nawet jeśli chcesz dowiedzieć się, jak działają, prawdopodobnie popełnisz kilka standardowych błędów na drodze do zbudowania parsera, który jest wystarczająco solidny, aby określić, czy dany ciąg z Big Bad Web może być bezpiecznie przekazany do PHP eval() - i chociaż nie lubimy ignorancji, nie lubimy również wrażliwych aplikacji internetowych. Po drugie, nie sądzę, że możliwe jest użycie generatora parserów (nawet powierzchownie) bez uczenia się czegoś nowego i użytecznego, nawet jeśli ostatecznie zdecydujesz się na inne podejście. – Peter

3

miarę walidacja jest zaniepokojony, następujące żetony postaci są ważne:

operator: [/*+-] 
funcs: (a\(|b\() 
brackets: [()] 
numbers: \d+(\.\d+)? 
space: [ ] 

Prosta walidacja może następnie sprawdzić, czy ciąg wejściowy dopasowuje dowolną kombinację tych wzorów. Ponieważ funcs żeton jest bardzo precyzyjne i nie kolidować wiele innych tokenów, to walidacja powinna być dość stabilna w/o konieczności wykonania jakichkolwiek składni/gramatyki już:

$tokens = array(
    'operator' => '[/*+-]', 
    'funcs' => '(a\(|b\()', 
    'brackets' => '[()]', 
    'numbers' => '\d+(\.\d+)?', 
    'space' => '[ ]', 
); 

$pattern = ''; 
foreach($tokens as $token) 
{ 
    $pattern .= sprintf('|(?:%s)', $token); 
} 
$pattern = sprintf('~^(%s)*$~', ltrim($pattern, '|')); 

echo $pattern; 

Tylko jeśli cały ciąg wejściowy mecze przeciwko wzór oparty na tokenie, sprawdza poprawność.To wciąż może być składniowo źle PHP, umieścić można zapewnić, że tylko opierać się na określonych tokeny:

~^((?:[/*+-])|(?:(a\(|b\())|(?:[()])|(?:\d+(\.\d+)?)|(?:[ ]))*$~ 

Jeśli budować wzorzec dynamicznie - jak na przykład - jesteś w stanie zmienić swoje żetony językowe później na łatwiejszym.

Dodatkowo może to być pierwszy krok do własnego tokenizera/lexera. Strumień tokenów może następnie zostać przekazany do analizatora składni, który może je zweryfikować i zinterpretować. To jest część user187291 wrote about.

Alternatywnie do napisania pełnego lexera + analizatora składni i musisz sprawdzić poprawność składni, możesz również sformułować swoją gramatykę w oparciu o tokeny, a następnie utworzyć gramatykę opartą na regex na tokenowej reprezentacji danych wejściowych.

Tokeny to słowa, których używasz w swojej gramatyce. Konieczne będzie dokładniejsze opisanie nawiasów i definicji funkcji w tokenach, a tokenizer powinien przestrzegać bardziej przejrzystych reguł, których token zastępuje inny token. Pojęcie jest opisane w another question of mine. Używa również regex do formułowania gramatyki i sprawdzania składni, ale nadal nie parsuje. W twoim przypadku eval będzie parserem, z którego korzystasz.

+0

[Symuluj język PHP w budowie lub parsuj z regexp?] (Http://stackoverflow.com/questions/3267951/simulate-php-array-language-construct-or-parse-with-regexp/3268443#3268443) – hakre

0

Można użyć token_get_all(), sprawdzić każdy token i przerwać na pierwszym nieważnym tokenie.

0

Odpowiedź hakre'a, użycie wyrażenia regularnego jest dobrym rozwiązaniem, ale jest trochę skomplikowane. Również obsługa białej listy funkcji staje się raczej nieporządna. A jeśli to pójdzie źle, może to mieć bardzo nieprzyjemny wpływ na twój system.

Czy istnieje powód, dla którego nie używasz "eval" javascript?

+0

I trzeba pobrać niektóre dane z bazy danych do formuły, więc nie mogę używać JS. – hidarikani

Powiązane problemy