Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Dynamiczne typowanie to zło
Forum PHP.pl > Forum > PHP
kiler129
Witajcie!
Spotykam się z tym problemem po raz pierwszy, rozłożyłem już nawet short-ifa na kawałki i nadal dostaję bzdurę:

  1. $x="***ANY***";
  2. $y = (int)$x;
  3. $z = $x."";
  4.  
  5. var_dump(($y == $z), $y, $z);


Wynik:
Kod
bool(true) int(0) string(9) "***ANY***"

Co on u diaska robi, że wychodzi mu true? smile.gif
JoShiMa
Cytat(kiler129 @ 19.03.2011, 00:11:42 ) *
Co on u diaska robi, że wychodzi mu true? smile.gif

Prawidłowo postawione pytanie brzmi: Co Ty robisz? A Ty porównujesz zmienne a jak jedna z nich jest strungiem to pewnie lepiej jest posłużyć się tym:

http://pl2.php.net/manual/en/function.strcmp.php
Crozin
Używasz operatora porównania tak więc PHP wykryje, że operandy są różnych typów po czym spróbuje prawy operand zrzutować* do typu lewego operandu. Tak, więc "***ANY***" zostanie zamienione na typ całkowity, czyli na 0. A zero jest zeru równe więc całe wyrażenie zwróci prawdę.

* błędnie to nazwałem, ale nie wiem jak lepiej to po polsku określić.
kiler129
No tak ale w takim razie jak wymusić porównanie tego typu aby nie zostało to zrzutowane?
Chyba jedynym sposobem jest takie coś:

  1. $x="***ANY***";
  2. $y = (string)(int)$x;
  3. $z = (string)$x."";
  4. var_dump(($y == $z), $y, $z);
  5.  
  6. //I foma docelowa-skrócona:
  7. $result = ((string)(int)$x === (string)$x.""));
  8. var_dump($result);


Dlaczego tak kombinuje? Potrzebuję sprawdzić zmienną czy jest liczbą całkowitą zapisaną 0-9 (bez notacji naukowych itp).
Ani is_int() ani is_numeric() nie spełnia tego założenia a użycie tutaj preg`a to hipokryzja wink.gif
Crozin
Pytasz jak wymusić porównanie bez rzutowania po czym sam jawnie wykonujesz trzykrotne rzutowanie. Po prostu użyj operatora identyczności (===), który najpierw sprawdza typ, a potem zawartość zmiennej.

Co do problemu jest kilka sensownych rozwiązań:
1. Zrzutuj do typu całkowitego, sprawdź czy liczba jest z przedziału 0 - 9 i olej to czy ktoś wpisał tam "1.1213321321" (1) albo "ala ma kota" (0). Naprawdę nie ma potrzeby sprawdzania w jakiej postaci coś dotarło pierwotnie, skoro po rzutowaniu ma jedną z akceptowalnych wartości.
2. Sprawdź czy tekst składa się z dokładnie jednego znaku i czy tekst ten jest liczbą
kiler129
Ad 1. Na pierwszy rzut oka tak, ale o ile wiem w php nie można sprawdzić do zbioru liczb całkowitych a przecież ja nie chce if($x > 0 && $x < 9) return true; tylko potrzbuję aby dowolny prawidłowy int w notacji arabskiej był przepuszczany
Ad 2. Też się nie sprawdzi dla np. 44 czy -44 smile.gif
Crozin
1. Możesz rzutować do typu całkowitego ((int) $zmienna) i po takim rzutowaniu wystarczy sprawdzić dokładnie tak jak w Twoim przykładzie. Co Ci przeszkadza, że ktoś prześle " 2" (2) albo "423ssdsa42da" (423 - w dalszym etapie wyłapiesz, że jest to niepoprawne) skoro nie ma to najmniejszego wpływu na bezpieczeństwo czy sprawność pracy skryptu?
2. Jak Ci się sprawdzi skoro pierwszy zapis to dwa znaki, a drugi 3? Przeczytaj jeszcze raz początek drugiego punktu.
kiler129
Ad. 1: Przeszkadza wink.gif Skoro ktoś wysyła mi " 44" to znaczy, że mam bug w JS albo "CHaker" coś majstruje przy zapytaniach smile.gif
Założenie jest proste jak budowa cepa - sprawdź czy to co dostałeś to liczba całkowita [kropka]

Ad 2. Yyy.. to właśnie, że się NIE sprawdzi smile.gif Chyba już obaj śpimy Lkingsmiley.png
Crozin
1. Każdy zawsze może Ci przesłać dowolne dane to raz. Dwa:
  1. $zmienna = (int) $_GET['zmienna'];
  2.  
  3. if ($zmienna < 0 || $zmienna > 9) {
  4. die('błąd');
  5. }
  6.  
  7. // dane w $zmienna są na 100% bezpieczne
A jeżeli już tak bardzo zależy Ci na tym by sprawdzić czy w $_GET['zmienna'] pierwotnie była liczba całkowita z zakresu 0 - 9 to dodaj sobie przed pierwszym IFem
  1. if ($_GET['zmienna'] !== (string) (int) $_GET['zmienna']) {
  2. die('nie oszukuj');
  3. }
Chociaż celowość czegoś takiego jest raczej znikoma.
2. To Ty się wyśpij i jutro jeszcze raz przeczytaj.
JoShiMa
Cytat(kiler129 @ 19.03.2011, 00:39:08 ) *
No tak ale w takim razie jak wymusić porównanie tego typu aby nie zostało to zrzutowane?

Dwa posty wyżej podałam Ci funkcję.

A jeśli naprawdę zależy Ci na porządnym filtrowaniu to niestety wyrażenia regularne Cię nie ominą. Sprawdzasz, czy przekazany ciąg znaków składa się z samych cyfr i robisz to w jednej linijce, bez rzutowania porównywania i cudowania.
kiler129
Chyba nie zrozumiałeś idei - nie chodzi o sprawdzenie czy liczba jest z zakresu 0-9 tylko czy wysłana liczba jest zapisana notacją arabską bez wersji naukowych, np.: 10, 15, -3, 11, -111.
Twój ostatni if już bardziej tutaj zadziała ale lepiej ręcznie zrobić rzutowanie bo przy porównywaniu PHP może zrzutować automatycznie (nie ma gwarancji ze zmienna to string).

edit: @up co mi da strcmp()? smile.gif Przecież ja nie porównuje 2 wartości a sprawdzam jedną czy spełnia odpowiednie warunku. Zamysł jest prosty: sprawdź czy podana zmienna zawiera TYLKO liczbę opisaną zbiorem liczb całkowitych zapisaną za pomocą cyfr arabskich z uwzględnieniem minusa.
Crozin
Cytat
[...] (nie ma gwarancji ze zmienna to string).
PHP jasno określa co zostanie na co zrzutowane w jakim wypadku. Tak więc jest gwarancja.

  1. if (strlen($_GET['zmienna']) == 1 && ctype_digit($_GET['zmienna'])) {
  2. // jest poprawna
  3. }
  4.  
  5. ----------------------
  6.  
  7. $zmienna = (int) $_GET['zmienna'];
  8. if ($zmienna >= 0 && $zmienna <= 9 && $zmienna == $_GET['zmienna']) {
  9. // jest poprawna
  10. }
Oba warunki sprawdzą czy Ci przesłano coś co jest cyfrą arabską.

EDIT: Jak ma jeszcze uwzględniać liczby ujemne to zostałbym przy drugim rozwiązaniu (pierwsze wymagałoby za dużo modyfikacji).
zegarek84
@kiler129
Kod
if(preg_match('/^\-?[0-9]+$/', $_GET['zmienna'])) {
    \\...
}
kiler129
@Crozin - nauczyłem się nie do końca wierzyć w takich sprawach automatom php. Z wersji na wersje niektóre rzeczy się zmieniają - szczególnie takie niuanse.
Pisząc kod staram się aby napisany w 2011 roku bez większych poprawek działał poprawnie i bezpiecznie również 2020 roku.

Co do kodu który proponujesz to czy ty czytasz co ja pisze? closedeyes.gif
To co podałeś sprawdza czy podana liczba znajduje się w zbiorze {0,1,2,3,4,5,6,8,9} a nie czy jest zapisana za pomocą znaków ze zbioru {0,1,2,3,4,5,6,8,9} (+ ew minus);

Cytat(zegarek84 @ 19.03.2011, 16:45:37 ) *
@kiler129
Kod
if(preg_match('/^\-?[0-9]+$/', $_GET['zmienna'])) {
    \\...
}


Odpowiem kodem:
  1. <?php
  2. function c1($x) {
  3. return ((string)(int)$x === (string)$x."");
  4. }
  5.  
  6. function c2($x) {
  7. return preg_match('/^\-?[0-9]+$/', $x);
  8. }
  9.  
  10. $start = microtime(true);
  11. for($i=0;$i <= 1000000;$i++) {
  12. c1(rand(-100,100));
  13. }
  14. echo "Exec time: ".(microtime(true)-$start)." sec<br>";
  15.  
  16. $start = microtime(true);
  17. for($i=0;$i <= 1000000;$i++) {
  18. c2(rand(-100,100));
  19. }
  20. echo "Exec time: ".(microtime(true)-$start)." sec<br>";
  21. ?>


Wynik:
Kod
Exec time: 2.38471293449 sec
Exec time: 4.2167160511 sec



Można by powiedzieć: eee, 2 sec na milion powtórzeń to nic ale jakbym wszędzie wpychał regexy to działałoby to fatalnie.
Crozin
  1. $_GET['zmienna'] == (int) $_GET['zmienna']
Sprawdzi Ci czy przesłane dane do liczba arabska (z uwzględnieniem minusa).

Cytat
Z wersji na wersje niektóre rzeczy się zmieniają - szczególnie takie niuanse.
http://www.php.net/manual/en/types.comparisons.php - jak widzisz od początku (albo przynajmniej od wielu lat) nic się tu nie zmieniło.
zegarek84
Cytat(kiler129 @ 19.03.2011, 17:29:36 ) *
Można by powiedzieć: eee, 2 sec na milion powtórzeń to nic ale jakbym wszędzie wpychał regexy to działałoby to fatalnie.

wowć - w pierwszej kolejności nie pasowało Ci rzutowanie typu zmiennej, potem inne rzeczy, wyrażenie też nie - ogólnie nic Ci nie pasuje i z tego co widać po postach nic Ci nie ma prawa spasować - jeśli chodzi o typowanie zmiennych podczas deklaracji to przerzuć się na c++ lub jave, przy czym ten pierwszy będzie szybszy od drugiego [chyba, że skopiesz algorytmy] - a jeśli już tak nic Ci nie pasuje i jesteś fanem szybkości to zacznij pisać w ASM'ie - napisz sobie obsługę socketów samemu optymalnie [sprawdź każde z optymalnych rozwiązań funkcji], dalej to obuduj swoim serwerem i będziesz hapi gdyż w ASMie będziesz miał kontrolę niemal absolutną i szybkość powalającą ;]

poza tym skoro aż tak optymalny chcesz kod to dlaczego piszesz $i++ zamiast ++$i o.O - o ile w językach kompilowalnych to w zasadzie nie robi różnicy o tyle w skryptowych preinkrementacja jest szybsza od post inkrementacji.... i idąc dalej skoro wiesz, że itteracji jest parzysta liczba dlaczego for'a nie napiszesz w stylu:
for($i=0;$i<1000000;++$i){
c1(rand(-100,100));
++$i;
c1(rand(-100,100));
}
baaa - a tym bardziej, że jest podzielne np. przez 10 dlaczego nie zoptymalizowałeś tego jeszcze o.O - no proszę Cię - to nie wiesz, że wtedy pętla jest szybsza i ma mniej sprawdzeń warunku?? o.O - mniej nie jawnych if'ów... aż tak kładziesz nacisk na optymalizację a nie widać byś sam cokolwiek specjalnie ostro optymalizował o.O
kiler129
@Crozin - niestety nie mam pewności czy nie dostanę bool true - wtedy wynik będzie true. Uprzedzając pytanie - jeśli użyje === odcinam się od stringów.

@zegarek84 - nastąpiłem na odcisk? ;]
Tu nie chodzi o durną optymalizację ale proste kroki które mogą poprawić wydajność (np. petla for zamiast foreach w tablicach samomodyfikujących) Jest dużo skryptów obficie używających regexa które przez powolność PHP przy wyrażeniach regularnych nie są za szybkie.
A co do przerzutki: do c++ się przymierzam, asm to zbytni hardcore wink.gif


Podsumowując - myślę, że temat się wyczerpał i nie ma po co go ciągnąć dalej - nic nowego tu nie zostanie już powiedziane a jedyne co możemy tu uzyskać to wzajemną wrogość i uszczypliwość.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.