• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Niezwykle rzadki problem z kodowaniem znaków PHP.

VPS Starter Arubacloud
+1 głos
597 wizyt
pytanie zadane 6 lipca 2020 w PHP przez Hype Początkujący (340 p.)

Cześć! Przejrzałem cały internet, szukałem w wielu miejscach i nikt nie potarfi rozwiązać tego problemu, który jest banalnie prosty do przedstawienia, ale brak w nimi logiki bo sam nie rozumiem dlaczego właściwie się to dzieje.

Proszę o pomoc, przedstawiam kod i powiem o co chodzi:

function IsLetterMalicious($_letter)
{
    $allowedCharacters = array(
        'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o','p', 'a', 's', 'd',
        'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n','m',

        'ę', 'ó', 'ą', 'ś', 'ł', 'ż', 'ź', 'ć', 'ń',
        
        '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
      
        '_'
    );

        for($i=0; $i<sizeof($allowedCharacters); $i++)
        { 
            if(strcmp($_letter, $allowedCharacters[$i]) ==0)
            {
               return False;
            }      
        }
        return True;
}

Kod ten to funkcja która zwraca True lub False. Prawda? Nie zmienia ona $_letter czyli argumentu funkcji.

Otóż kod, w którym występuje błąd:

  echo($allowedCharacters[26]);
                    if($allowedCharacters[26] == "ę")
                    {
                      echo($allowedCharacters[26]);
                    }
                    for($i=0; $i<strlen($login); $i++)
                    {
                      echo($login[$i]);
                      if(IsLetterMalicious($login[$i]))
                      {
                        echo($login[$i]); 
                      } // Tutaj wyświetla się 'k?k?' zamiast 'kękę' a wcześniej normalnie 'kękę' widoczne było na stronie, po tym "booleanie" nagle coś się psuje.
                    }

 Zakomentowałem to co tam się dzieje, po sprawdzeniu funkcji przez boolean nagle kodowanie znaków UTF-8 się tak jakby wyłącza. Ktoś ma pomysł jak to naprawić?

Output wygląda w ten sposób dla stringu 'kękę':

image

widzimy na początku próbowałem wpisać do inputu rootkękę i było ok, potem próbowałem dalej zdania z tym znakiem polskim 'ę' i po wywołaniu funkcji, która zwraca boola nagle znaki są '?' co nie ma kompletnie sensu. Błagam, ja tak dalej już nie dam rady pracować.

1
komentarz 6 lipca 2020 przez VBService Ekspert (251,210 p.)
edycja 6 lipca 2020 przez VBService

Uruchom ten kod, może zobaczysz gdzie problem: wink

detect_encoding.php

<!DOCTYPE html>
<html lang="pl">
    <head>
        <meta charset="utf-8">
    </head>
    <body>
    <pre>
<?php

    $login = "rootkękęóąśł";
    echo $login . PHP_EOL;
    for($i=0; $i<mb_strlen($login); ++$i) {
        echo $login[$i] . " = " . mb_detect_encoding($login[$i]) . PHP_EOL;
    }

    echo PHP_EOL;

    $allowedCharacters = array(
            'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o','p', 'a', 's', 'd',
            'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n','m',
            'ę', 'ó', 'ą', 'ś', 'ł', 'ż', 'ź', 'ć', 'ń',
            '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
            '_'
     );

     for($i=0; $i<sizeof($allowedCharacters); $i++) {
        echo $allowedCharacters[$i] . " = " . mb_detect_encoding($allowedCharacters[$i]) . PHP_EOL;
     }

     echo PHP_EOL;

     echo "\xc4\x99";
     echo "\xc3\xb3"; // itd.
?>
   </pre>
    </body>
</html>

Miałem ten sam problem jeszcze w php 5xx, ale widzę, że w 7xx jest dalej.
Nie ma "automatycznej" konwersji, trzeba dalej kombinować. wink
Żeby porównać jakiekolwiek znaki diakrytyczne danego języka (polskie ę, ó, ś, ł itd.) muszą mieć identyczne kodowanie. Do porównania lepiej użyć odpowiedni regexp niż tablice pojedynczych znaków, bo możesz sprawdzić od razu całą zawartość zmiennej $login bez sprawdzania jej $login[$i] litera po literze. 

How to deal with Polish Characters while using regex?

Wtedy cały ten kod:

function IsLetterMalicious($_letter)
{
    $allowedCharacters = array(
        'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o','p', 'a', 's', 'd',
        'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n','m',
 
        'ę', 'ó', 'ą', 'ś', 'ł', 'ż', 'ź', 'ć', 'ń',
         
        '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
       
        '_'
    );
 
        for($i=0; $i<sizeof($allowedCharacters); $i++)
        { 
            if(strcmp($_letter, $allowedCharacters[$i]) ==0)
            {
               return False;
            }      
        }
        return True;
}

for($i=0; $i<strlen($login); $i++)
                  {
                    echo($login[$i]);
                    if(IsLetterMalicious($login[$i]))
                    {
                      echo($login[$i]); 
                    } // Tutaj wyświetla się 'k?k?' zamiast 'kękę' a wcześniej normalnie 'kękę' widoczne było na stronie, po tym "booleanie" nagle coś się psuje.
                  }

Zamieniasz do tego: wink


$login = "rootkękęóąśł";
if (preg_match("twoj_regexp", $login)) {
   echo "dozwolone znaki";
} else {
   echo "zawiera niedozwolone znaki";
}

 

komentarz 6 lipca 2020 przez Comandeer Guru (599,730 p.)

Huh, nie sądziłem, ze obsługa Unicode w PHP jest wciąż tak skopana. Na tyle, że praktycznie nie da się tego normalnie używać :/

komentarz 6 lipca 2020 przez Comandeer Guru (599,730 p.)

@VBService, zdecydowanie powinieneś z tego zrobić odpowiedź, bo to faktyczne rozwiązanie problemu.

komentarz 6 lipca 2020 przez Hype Początkujący (340 p.)

@VBService, Boże święty co się tutaj dzieje. Przestudiuję sobie to i dam znać, na pewno nie chce zrobić kopiuj+wklej i powiedzieć, że działa. Tak czy inaczej jak dotychczas to dziękuję za odpowiedź, muszę to teraz sprawdzić i zobaczyć gdzie ten błąd faktycznie był.

2
komentarz 6 lipca 2020 przez Comandeer Guru (599,730 p.)
Tak po prawdzie błąd nie jest w Twoim kodzie, tylko w totalnie skopanej obsłudze UTF-8 przez PHP…

2 odpowiedzi

+3 głosów
odpowiedź 6 lipca 2020 przez VBService Ekspert (251,210 p.)
wybrane 7 lipca 2020 przez Hype
 
Najlepsza

Uruchom ten kod, może zobaczysz gdzie problem: wink

detect_encoding.php

<!DOCTYPE html>
<html lang="pl">
    <head>
        <meta charset="utf-8">
    </head>
    <body>
    <pre>
<?php

    $login = "rootkękęóąśł";
    echo $login . PHP_EOL;
    for($i=0; $i<mb_strlen($login); ++$i) {
        echo $login[$i] . " = " . mb_detect_encoding($login[$i]) . PHP_EOL;
    }

    echo PHP_EOL;

    $allowedCharacters = array(
            'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o','p', 'a', 's', 'd',
            'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n','m',
            'ę', 'ó', 'ą', 'ś', 'ł', 'ż', 'ź', 'ć', 'ń',
            '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
            '_'
     );

     for($i=0; $i<sizeof($allowedCharacters); $i++) {
        echo $allowedCharacters[$i] . " = " . mb_detect_encoding($allowedCharacters[$i]) . PHP_EOL;
     }

     echo PHP_EOL;

     echo "\xc4\x99";
     echo "\xc3\xb3"; // itd.
?>
   </pre>
    </body>
</html>

Miałem ten sam problem jeszcze w php 5xx, ale widzę, że w 7xx jest dalej.
Nie ma "automatycznej" konwersji, trzeba dalej kombinować. wink
Żeby porównać jakiekolwiek znaki diakrytyczne danego języka (polskie ę, ó, ś, ł itd.) muszą mieć identyczne kodowanie. Do porównania lepiej użyć odpowiedni regexp niż tablice pojedynczych znaków, bo możesz sprawdzić od razu całą zawartość zmiennej $login bez sprawdzania jej $login[$i] litera po literze. 

How to deal with Polish Characters while using regex?

Wtedy cały ten kod:

function IsLetterMalicious($_letter)
{
    $allowedCharacters = array(
        'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o','p', 'a', 's', 'd',
        'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n','m',
 
        'ę', 'ó', 'ą', 'ś', 'ł', 'ż', 'ź', 'ć', 'ń',
         
        '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
       
        '_'
    );
 
        for($i=0; $i<sizeof($allowedCharacters); $i++)
        { 
            if(strcmp($_letter, $allowedCharacters[$i]) ==0)
            {
               return False;
            }      
        }
        return True;
}

for($i=0; $i<strlen($login); $i++)
                  {
                    echo($login[$i]);
                    if(IsLetterMalicious($login[$i]))
                    {
                      echo($login[$i]); 
                    } // Tutaj wyświetla się 'k?k?' zamiast 'kękę' a wcześniej normalnie 'kękę' widoczne było na stronie, po tym "booleanie" nagle coś się psuje.
                  }

Zamieniasz do tego: wink

$login = "rootkękęóąśł";
if (preg_match("twoj_regexp", $login)) {
   echo "dozwolone znaki";
} else {
   echo "zawiera niedozwolone znaki";
}

Zdaję sobie sprawę, że posługiwanie się regułami regexp nie jest łatwe, mimo wszystko polecam to rozwiązanie. [ preg_match ] [ Regular Expressions (Perl-Compatible) ]. Edycja, testowanie (w raz z wytłumaczeniem) regexp online.

(Mam taki regexp "zrobiony" wink, ale na chwilę obecną go nie podam, może za jakiś czas, nie chcę Ciebie jak i innych pozbawić tej przyjemności zrobienia tego samodzielnie).

Możesz dalej próbować z tablicą, tu pomocne polecenia php: [ array_map() ] [ utf8_decode ] [ mb_detect_encoding ] [ in_array ] [ mb_substr ] [ mb_strlen ]

How to convert php array to utf8?
PHP convert string to hex and hex to string
How to print Hexadecimal UTF-8 characters in PHP
Kodowanie polskich znaków
PHP and UTF-8 - polecam cały ten artykuł [ PHP Best Practices ]

1
komentarz 7 lipca 2020 przez Hype Początkujący (340 p.)

Dopiero dziś skończyłem uczyć się tego preg_match i udało się. Udostępniam gotowe rozwiązanie jak i dziękuję każdemu za odpowiedzi, bardzo jestem wdzięczny.

regex:

 if(preg_match("/^[a-z]|[0-9]|[ą]|[ó]|[ę]|[ś]|[ł]|[ż]|[ź]|[ć][ń]$/", $_letter))

Tutaj taki człowieczek dobrze tłumaczy te wszystkie zapisy:

https://www.youtube.com/watch?v=lumN4-pmSZ0

+ tutaj jest lista co zrobić z zapisami żeby stworzyć coś własnego, jak dla mnie to najbardziej pomogło:

Simple regex

Regex quick reference
[abc]     A single character: a, b or c
[^abc]     Any single character but a, b, or c
[a-z]     Any single character in the range a-z
[a-zA-Z]     Any single character in the range a-z or A-Z
^     Start of line
$     End of line
\A     Start of string
\z     End of string
.     Any single character
\s     Any whitespace character
\S     Any non-whitespace character
\d     Any digit
\D     Any non-digit
\w     Any word character (letter, number, underscore)
\W     Any non-word character
\b     Any word boundary character
(...)     Capture everything enclosed
(a|b)     a or b
a?     Zero or one of a
a*     Zero or more of a
a+     One or more of a
a{3}     Exactly 3 of a
a{3,}     3 or more of a
a{3,6}     Between 3 and 6 of a

options: i case insensitive m make dot match newlines x ignore whitespace in regex o perform #{...} substitutions only once

Dziękuję serdecznie też za linki u góry, były ciekawe i też z nich korzystałem!! Pozdrawiam serdecznie, jeśli ktoś czegoś nie rozumie a ma taki kłopot to postaram się też pomóc.

+2 głosów
odpowiedź 6 lipca 2020 przez Comandeer Guru (599,730 p.)
edycja 6 lipca 2020 przez Comandeer

Hm, a dlaczego nie skorzystasz po prostu z in_array? Dzięki temu Twoja funkcja sprawdzająca litery może być skrócona do:

function isLetterMalicious($_letter)
{
    $allowedCharacters = […];

    return in_array( $_letter, $allowedCharacters, true );
}

To wydaje się rozwiązywać problem. Dodatkowo w dalszej części używasz strlen, które może zwracać niepoprawne wartości dla stringów w UTF-8. Zamiast tego lepiej używać mb_strlen.

Edit: zaproponowane przeze mnie rozwiązanie jednak nie działa. Teoretycznie naprawia problem z wyświetlaniem znaków, ale opiera się na błędnych założeniach (dość niepojęte jest dla mnie to, że w 2020 roku w jakimś popularnym języku programowania nie działa iteracja po ciągu w UTF-8).

1
komentarz 6 lipca 2020 przez Hype Początkujący (340 p.)
Dziękuję za tę funkcję in_array, zawsze nauczyłem się czegoś nowego a jestem początkującym. :)

Podobne pytania

+1 głos
1 odpowiedź 811 wizyt
pytanie zadane 12 sierpnia 2017 w HTML i CSS przez Milesq Nałogowiec (32,020 p.)
+1 głos
2 odpowiedzi 1,116 wizyt
pytanie zadane 8 sierpnia 2016 w Java przez niezalogowany
+1 głos
2 odpowiedzi 467 wizyt

92,453 zapytań

141,262 odpowiedzi

319,088 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...