Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> Kontrola danych wejsciowych
kicaj
post 4.12.2007, 22:01:06
Post #1





Grupa: Zarejestrowani
Postów: 1 640
Pomógł: 28
Dołączył: 13.02.2003
Skąd: Międzyrzecz/Poznań

Ostrzeżenie: (0%)
-----


Jak optymalnie i bardziej uniwersalnie sprawdzac dane w kontrolerach (data input)?
Pisanie kontrolerow na zasadzie:
  1. <?php
  2. class NameController extends Controllers
  3. {
  4. function indexAction( $aParams )
  5. {
  6.  if( isset( $aParams['id'] ) 
  7.  {
  8. if( is_numeric( $aParams['id'] ) // ...
  9.  }
  10.  else
  11.  {
  12.  echo 'nie podano id!';
  13.  
  14. //...i tak w kazdej metodzie, sprawdzane rozne warunki z danych wejsciowych
  15. ?>


Sprawdzanie tego w kazdym kontrolerze i w jego kazdej metodzie mija sie z celem, jak to rozwiazac inaczej?


--------------------
PHP Developer

"Nadmiar wiedzy jest równie szkodliwy jak jej brak" Émile Zola
Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi (1 - 13)
Sedziwoj
post 4.12.2007, 23:01:10
Post #2





Grupa: Zarejestrowani
Postów: 793
Pomógł: 32
Dołączył: 23.11.2006
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Przez kontrole typów argumentów <lol> sorry nie ma tego w PHP dla typów podstawowych, bo mija się to z ideologią twórców ;]
Ja zawsze robiłem
  1. <?php
  2. if( !is_numeric( $intPar ){
  3.  throw new ParamException( 'Paramet musi być liczbą!' );
  4. }
  5. ?>

czy raczej is_int(), taka kontrola czy wszystko jest ok. Na wydajność nie patrzyłem, wolę aby działało poprawnie, a wydajność można zyskać optymalizując coś co ma większy kosz czasowy.


--------------------
Algorytmy w PHP, czy ktoś o tym słyszał?
Dlaczego tak mało kobiet programuje? ponieważ nie zajmują się głupotami.
Go to the top of the page
+Quote Post
kicaj
post 4.12.2007, 23:25:48
Post #3





Grupa: Zarejestrowani
Postów: 1 640
Pomógł: 28
Dołączył: 13.02.2003
Skąd: Międzyrzecz/Poznań

Ostrzeżenie: (0%)
-----


Wiem, sa rozne rodzaje funkcji.
Problem polega na tym ze jest to powtarzanie kodu co jest nie zgodne z programowaniem OOP i zasada DRY

Czesto tych instrukcji if jest duzo, co lubie, bo dokladnie user dostaje komunikat czego brakuje/co zle zrobil itp.


--------------------
PHP Developer

"Nadmiar wiedzy jest równie szkodliwy jak jej brak" Émile Zola
Go to the top of the page
+Quote Post
splatch
post 4.12.2007, 23:45:56
Post #4





Grupa: Zarejestrowani
Postów: 487
Pomógł: 7
Dołączył: 7.01.2004
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Kontrola danych wejściowych w miarę możliwości powinna być poza ciałem akcji. Jakkolwiek nie zależy zapominać że to konkretna akcja determinuje nam warunki walidacji.
Warunki typu czy to liczba, czy to jest wymagane i tak dalej można wyodrębnić do klas, które załatwią nam walidację. Jak zwykle w takim przypadku zaczynamy od interfejsu:
  1. <?php
  2. interface Validation {
  3. /**
  4. * @param $ctx Kontekst z informacjami na temat walidacji
  5. **/
  6. public function validate(ValidationContext $ctx);
  7. }
  8. ?>


Dobrze, teraz jak może wyglądać definicja kontekstu walidacji:
  1. <?php
  2. interface ValidationContext {
  3.  
  4. // tu mamy ważny błąd
  5. public function addError($key, ValidationMessage $msg);
  6.  
  7. // a tu powiedzmy, ostrzeżenie
  8. public function addNotice($key, ValidationMessage $msg);
  9.  
  10. public function addValidator(Validator $v);
  11.  
  12. // czy wszystko "ok"
  13. public function isPassed();
  14.  
  15. // mapa wiadomości gdzie kluczem jest nazwa pola a wartością kolekcja wiadomości
  16. public function getMessages();
  17.  
  18. // tutaj dane z requestu
  19. public function getArgument($key);
  20. }
  21. ?>


Dobra, dorzućmy do tego jeszcze wiadomość:
  1. <?php
  2. interface ValidationMessage {
  3.  
  4. // tutaj wiadomość, oczekujemy tylko tego - jak ona jest ustawiana
  5. // to już kwestia wtórna
  6. public function getMessage();
  7. }
  8. ?>


Może wydawać się dziwne to, że robię interfejs do takiego banału, aczkolwiek mam jeden cel:
  1. <?php
  2. abstract class AbstracMessage {
  3.  
  4. // nie można się dobrać do wiadomości nawet z potomków
  5. private $message;
  6.  
  7. public function __construct($message, array $args = array()) {
  8. $this->message = $this->transform($this->getMessageString($message), $args);
  9. }
  10.  
  11. protected abstract function getMessageString($message);
  12.  
  13. private function transform($string, array $args) {
  14.  // tu może być coś innego
  15.  return strtr($string, array_keys($args), array_values($args));
  16. }
  17.  
  18. // to nam wymusza odwołanie do konstruktora
  19. public final function getMessage() {
  20. return $this->message;
  21. }
  22. }
  23.  
  24. // ta wiadomość po prostu zwraca tekst, który był
  25. class RawMessage extends AbstractMessage {
  26. protected function getMessageString($message) {
  27. return $message;
  28. }
  29. }
  30.  
  31. // a ta wiadomość tłumaczy ładnie komunikaty
  32. class I18nMessage extends AbstractMessage {
  33.  
  34. private $resource;
  35.  
  36. public function __construct(I18nValidationContext $ctx, $message, array $args = array()) {
  37. $this->resource = $ctx->getI18Resource();
  38. parent::__construct($message, $args);
  39. }
  40.  
  41. protected function getMessageString($message) {
  42. return $this->resource->getString($message);
  43. }
  44. }
  45.  
  46. // to powtórka z rozrywki - mamy interfejs i dorabiamy implementacje
  47. interface MessageResource {
  48. public function getString($key);
  49. public function getStringArray($key);
  50. }
  51.  
  52. // implementacja do zrobienia
  53. interface I18nValidationContext extends ValidationContext {
  54. public function getResource();
  55. }
  56.  
  57. ?>


Dobra, wiadomości wiadomościami, ale przecież nie jest to najistotniejsze, najbardziej przecież nas interesuje powiązanie tego wszystkiego - czyli jak akcja "się waliduje". Zasadniczo znowu byłbym za interfejsem:
  1. <?php
  2. interface Action {
  3. public function execute();
  4. }
  5.  
  6. interface SecureAction {
  7. public function getCredentials();
  8. }
  9.  
  10. interface ValidationRequiredAction {
  11. public function validate(ValidationContext $ctx);
  12. }
  13. ?>

Mając takie definicje front controller, który ma podniesiony ValidationContext może rozróżnić co zrobić - czyli najpierw sprawdza czy akcja wymaga autoryzacji - jeśli tak sprawdza uprawnienia, następnie weryfikuje czy dane, które przychodzą są ok.

Teraz rzecz zasadnicza - czyli co z konfiguracją. Jak widać same walidatory to pestka - ale jak zrobić to ładnie. No więc - może przykładowa implementacja:
  1. <?php
  2.  
  3. class DefaultValidationContext implements ValidationContext {
  4.  
  5. private $errors = 0;
  6.  
  7. // tu mamy ważny błąd
  8. public function addError($key, ValidationMessage $msg) {
  9. $this->addMessage(new StackEntry('error', $key, $msg));
  10. ++$this->errors;
  11. }
  12.  
  13. // a tu powiedzmy, ostrzeżenie
  14. public function addNotice($key, ValidationMessage $msg) {
  15. $this->addMessage(new StackEntry('notice', $key, $msg));
  16. }
  17.  
  18. protected final function addMessage(StackEntry $msg) {
  19. if (!isset($this->message[$msg->getKey()])) {
  20. $this->message[$msg->getKey()] = array();
  21. }
  22. $reference = &$this->message[$msg->getKey()];
  23. // teraz w widoku/akcji mamy do dyspozycji info o "powadze" błędu
  24. // oraz powiązaną z nim wiadomość
  25. $reference[sizeof($reference)]['severity'] = $msg->getSeverity();
  26. $reference[sizeof($reference)]['msg'] = $msg->getValidationMessage();
  27. }
  28.  
  29. // czy wszystko "ok"
  30. public function isPassed() {
  31. $i = 0;
  32. $validatorsCount = sizeof($this->validators);
  33. do {
  34. // jeśli coś się tu stanie to zwiększy nam się rozmiar tablicy $errors
  35. $this->validators[$i]->validate($this);
  36. } while ($this->errors == 0 && ++$i < $validatorsCount);
  37.  
  38. // przeszliśmy wszystkie walidatory i nie ma błędów
  39. return ++$i == $validatorsCount && $this->erros == 0;
  40. }
  41.  
  42. // mapa wiadomości gdzie kluczem jest nazwa pola a wartością kolekcja wiadomości
  43. public function getMessages() {
  44. return $this->message;
  45. }
  46.  
  47. public function addValidator(Validator $v) {
  48. $this->validators[] = $v;
  49. }
  50.  
  51. }
  52.  
  53.  
  54. ?>



No i na końcu akcja
  1. <?php
  2. class MyAction implements SecureAction, ValidationRequiredAction {
  3.  
  4. // dorzucamy co trzeba do kontekstu - resztę załatwia za nas FrontController
  5. public function validate(ValidationContext $ctx) {
  6. $ctx->addValidator(new NotEmptyValidator('user_id'));
  7. $ctx->addValidator(new NumberFormatValidator('user_id'));
  8. }
  9.  
  10. }
  11. ?>


Aa i jeszcze walidatory:
  1. <?php
  2. class NotEmptyValidator implements Validator {
  3.  
  4. private $field;
  5.  
  6. public function __construct($field) {
  7. $this->field = $field;
  8. }
  9.  
  10. public function validate(ValidationContext $ctx) {
  11. if (empty($ctx->getArgument($field)) {
  12. $ctx->addError(new RawMessage('Wartość {0} jest pusta', array($field)));
  13. }
  14. }
  15. }
  16. ?>


Kilka uwag odnośnie kodu, który jest wyżej .. przede wszystkim walidatory dodają wiadomości - równie dobrze mogą bronić się wyjątkami, aczkolwiek mamy wówczas problem ze sformułowaniem komunikatu dla użytkownika. Można zatem rozważyć dwie opcje - wydelegować formatowanie wiadomości z AbstractMessage do ValidationContext - wówczas zmieniły by się nam sygnatury metod addError, addNotice. Druga opcja to przesunąć formatowanie wiadomości do akcji.
Dlaczego dodałem coś takiego jak notice w walidacji? Są to informacje, które nie wywalają całego procesu, a które można wyświetlić przy ponownym wyświetlaniu błędu - powiedzmy ktoś nie podał wartości w wymaganym polu a w innym gdzie był powinien trafić float dostaliśmy int - dodajemy notice o konwersji.

Aaa i jeszcze walka z tworzeniem walidatorów:
  1. <?php
  2. class XmlValidationForm implements Action {
  3.  
  4. public final function validate(ValidationContext $ctx) {
  5. $path = $this->getContextPath();
  6. $binder new XmlValidatorBinder($path, $ctx);
  7. }
  8.  
  9. public function getContextPath() {
  10. return get_class($this) .'-validation.xml';
  11. }
  12.  
  13. }
  14.  
  15. class MyAction extends XmlValidationForm implements SecureAction, ValidationRequiredAction {
  16.  
  17. // tu już nic nie musimy dodawać - na podstawie nazwy klasy zostanie wczytana
  18. // konfiguracja walidatorów
  19.  
  20. }
  21. ?>


No i kolejna ciekawa wariacja nad którą można pomyśleć:
  1. <?php
  2. $orValidator = new OrValidationCondition($validator1, $validator2);
  3. $ctx->addValidator($orValidator);
  4. ?>


Kod który przedstawiłem nie jest ani kompletny ani nadzwyczajnie spójny. Może stanowić podstawę do rozpoczęcia prac, aczkolwiek pozostaje jeszcze kilka problemów, które wymagają niezłej gimnastyki. smile.gif


--------------------
Łukasz Dywicki
Independent Java and open source software consultant.
Blog - Java, OSGi, integracja oprogramowania..
Go to the top of the page
+Quote Post
Sedziwoj
post 5.12.2007, 00:04:56
Post #5





Grupa: Zarejestrowani
Postów: 793
Pomógł: 32
Dołączył: 23.11.2006
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


No tak jak zwykle czegoś nie doczytałem ;]
Bo ja nie o tej kontroli pisałem, tylko już "wewnętrznej"...

@splatch Znów spore ilości kodu umieszczasz biggrin.gif


--------------------
Algorytmy w PHP, czy ktoś o tym słyszał?
Dlaczego tak mało kobiet programuje? ponieważ nie zajmują się głupotami.
Go to the top of the page
+Quote Post
splatch
post 5.12.2007, 00:10:16
Post #6





Grupa: Zarejestrowani
Postów: 487
Pomógł: 7
Dołączył: 7.01.2004
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Cytat(Sedziwoj @ 5.12.2007, 00:04:56 ) *
@splatch Znów spore ilości kodu umieszczasz biggrin.gif

@Sedziwoj odpowiedzi na forum to obecnie mój jedyny sposób na utrzymanie kontaktu z PHP, stąd tak duże ilości kodu.

Myślę też, że takie przykłady z wykorzystaniem interfejsów, abstrakcji i tak dalej są jak najbardziej na miejscu i pomagają odwiedzającym bardziej niż mega rozwlekłe opisy i dywagacje. smile.gif Po prostu można zobaczyć "Object Oriented Programing in Action". winksmiley.jpg


--------------------
Łukasz Dywicki
Independent Java and open source software consultant.
Blog - Java, OSGi, integracja oprogramowania..
Go to the top of the page
+Quote Post
Sedziwoj
post 5.12.2007, 00:47:58
Post #7





Grupa: Zarejestrowani
Postów: 793
Pomógł: 32
Dołączył: 23.11.2006
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Wież tylko że dywagacje i rozwlekłe opisy się rozumie czytając, ty jednak przy kodzie trzeba chwilę pomyśleć zanim się zrozumie założenia twórcy. Choć ciągle mi się przypomina "witaj świecie" w wzorcach projektowych, czyli aby gdzieś przypadkiem forma nie przerosła treść. Tak jak dziś od dłuższego czasu napisałem coś w pełni proceduralne, ale nie widzę sensu aby dla 3 linijek jednokrotnego wykorzystania (coś do obliczeń dla koleżanki) pisać więcej.
Ale zawsze patrzenie co inni tworzą jest przydatne, tego nie krytykuję smile.gif

Tak z grubsza przed snem przejrzałem, na pewno są nieścisłości w tym kodzie, ale ogólną koncepcję chyba załapałem. Po prostu rozdzielenie funkcjonalności i umożliwienie stosowania różnych wariantów.
A jeszcze co do łączenia walidatorów warunkami, to ja chyba był bym za zdefiniowaniem, które połączyć i jako trzeci argument jaki warunek pomiędzy nimi. Do tego wartości 'notice' (w StackEntry()) zastąpił bym stałą, podobnie ten trzeci argument, warunek złączenia, walidatorów też jako stałą w obiekcie (czy interfejsie, już tego nie będę rozstrzygał :])

No dobra mykam spać.

Edit takie tam przecinki, kropi i tak pewnie kiepskie jest to co napisałem

Ten post edytował Sedziwoj 5.12.2007, 00:51:17


--------------------
Algorytmy w PHP, czy ktoś o tym słyszał?
Dlaczego tak mało kobiet programuje? ponieważ nie zajmują się głupotami.
Go to the top of the page
+Quote Post
splatch
post 5.12.2007, 08:44:35
Post #8





Grupa: Zarejestrowani
Postów: 487
Pomógł: 7
Dołączył: 7.01.2004
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Tak, zasadniczo skłaniając się w stronę opisu.

Validator element zajmujący się walidacją, dostaje kontekst i z niego musi wyciągnąć to, czego potrzebuje i przeprowadzić walidację. Kwestia konfiguracji walidatora - albo przez akcesory albo przez konstruktory.

ValidationContext zawiera informacje na temat całego procesu. Ma również zgromadzone dane wejściowe i udostępnia je walidatorom.

FrontController element, który nie jest tu widoczny, a który zasadniczo odpowiada za odpalenie akcji i zweryfikowanie czy wszystko "ok", innymi słowy robi $action->validate($this->validationContext), $this->validationContext->isPassed() - gdy dostajemy false podejmuje odpowiednie działania - jakie to zależy już od implementacji.

ValidationRequiredAction typ, który sugeruje FrontControllerowi, że dana akcja chciałaby otrzymywać dane, które były zweryfikowane. Jest to tylko interfejs, pod spodem możemy w różny sposób dodawać do kontekstu swoje walidatory. Mogą być one odczytane z XML, mogą być wpisane z palca. Co kto woli.

I18nValidationContext kontekst dostarczający "tłumaczenia".

I18nMessage wiadomość, która zostanie przetłumaczona, przekazujemy do konstruktora klucz, na podstawie którego z kontekstu zostanie pobrana odpowiednia wiadomość.

OrValidationCondition walidator prezentujący prosty warunek logiczny, to lub to. Jesteśmy wolni od problemów związanych z tym, że przychodzi nam raz to raz to.


--------------------
Łukasz Dywicki
Independent Java and open source software consultant.
Blog - Java, OSGi, integracja oprogramowania..
Go to the top of the page
+Quote Post
Cysiaczek
post 5.12.2007, 10:11:05
Post #9





Grupa: Moderatorzy
Postów: 4 465
Pomógł: 137
Dołączył: 26.03.2004
Skąd: Gorzów Wlkp.




@splatch - w jaki sposób ładujesz (fizycznie) taką ilość interfejsów? Przeanalizowałem Twoje ostatnie posty i tak sobie pomyślałem, że starałem się unikać interfejsów na rzecz abstrakcji również z tego powodu, że autoloader w końcu by wymiękł ;p

Pozdrawiam.


--------------------
To think for yourself you must question authority and
learn how to put yourself in a state of vulnerable, open-mindedness;
chaotic, confused, vulnerability, to inform yourself.
Think for yourself. Question authority.
Go to the top of the page
+Quote Post
Sedziwoj
post 5.12.2007, 10:39:46
Post #10





Grupa: Zarejestrowani
Postów: 793
Pomógł: 32
Dołączył: 23.11.2006
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


@Cysiaczek
Nie przesadzaj dla autoloader'a to nie jest kłopot, gorzej tylko z nazewnictwem ;]


--------------------
Algorytmy w PHP, czy ktoś o tym słyszał?
Dlaczego tak mało kobiet programuje? ponieważ nie zajmują się głupotami.
Go to the top of the page
+Quote Post
splatch
post 5.12.2007, 10:50:09
Post #11





Grupa: Zarejestrowani
Postów: 487
Pomógł: 7
Dołączył: 7.01.2004
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Cytat(Cysiaczek @ 5.12.2007, 10:11:05 ) *
@splatch - w jaki sposób ładujesz (fizycznie) taką ilość interfejsów? Przeanalizowałem Twoje ostatnie posty i tak sobie pomyślałem, że starałem się unikać interfejsów na rzecz abstrakcji również z tego powodu, że autoloader w końcu by wymiękł ;p


Kod piszę na poczekaniu, na potrzeby poszczególnych postów. Wydaje mi się, że interfejsy w tym przypadku nie są problematyczne, są małe i lekkie, część z nich zawiera tylko po parę metod.
Co do optymalizacji autoloadera - w Mojavi 3 metoda była prosta - stworzenie pliku z definicjami wszystkich wymaganych interfejsów etc. Wczytywany był 1 plik a nie kilka.


--------------------
Łukasz Dywicki
Independent Java and open source software consultant.
Blog - Java, OSGi, integracja oprogramowania..
Go to the top of the page
+Quote Post
kicaj
post 5.12.2007, 12:04:12
Post #12





Grupa: Zarejestrowani
Postów: 1 640
Pomógł: 28
Dołączył: 13.02.2003
Skąd: Międzyrzecz/Poznań

Ostrzeżenie: (0%)
-----


Dobra dobra, bo troche odbiegacie od tematu...

To co splatch wklepal jest troche za bardzo przesadzaone, pytanie brzmialo jak z zasada DRY robic kontrole danych wejsciowych, tzn. jak nie powtarzac sie sprawdzajac dane z parametrow metody, takich jak id, liczba, string, itp


--------------------
PHP Developer

"Nadmiar wiedzy jest równie szkodliwy jak jej brak" Émile Zola
Go to the top of the page
+Quote Post
splatch
post 5.12.2007, 12:11:53
Post #13





Grupa: Zarejestrowani
Postów: 487
Pomógł: 7
Dołączył: 7.01.2004
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Cytat(kicaj @ 5.12.2007, 12:04:12 ) *
To co splatch wklepal jest troche za bardzo przesadzaone, pytanie brzmialo jak z zasada DRY robic kontrole danych wejsciowych, tzn. jak nie powtarzac sie sprawdzajac dane z parametrow metody, takich jak id, liczba, string, itp

Jeśli przykład kodu, który jest wyżej koliduje z DRY to proszę pokaż mi gdzie. Być może źle rozumiem to pojęcie i gdzieś popełniłem błąd. Ewentualnie powiedz co w moim przykładzie odbiega od Twoich oczekiwań.


--------------------
Łukasz Dywicki
Independent Java and open source software consultant.
Blog - Java, OSGi, integracja oprogramowania..
Go to the top of the page
+Quote Post
kicaj
post 5.12.2007, 17:34:50
Post #14





Grupa: Zarejestrowani
Postów: 1 640
Pomógł: 28
Dołączył: 13.02.2003
Skąd: Międzyrzecz/Poznań

Ostrzeżenie: (0%)
-----


No stworzylem sobie cos na ten wzor, ale duzo duzo mniej kodu, tzn. z czasem bedzie to sie rozrastalo

Beda problemy, beda pytania smile.gif


--------------------
PHP Developer

"Nadmiar wiedzy jest równie szkodliwy jak jej brak" Émile Zola
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 14.08.2025 - 03:00