Łączenie stringów i zmiennych w PHP
W PHP mamy kilka opcji określania stringów, ale większość z nas jest zaznajomiona jedynie z ich definiowaniem za pomocą cudzysłowów. Czasami te stringi są proste i możemy ich używać w obecnej postaci, ale najczęściej chcemy, aby były dynamiczne, połączone z różnymi zmiennym.
Ważne jest, aby dobrze zrozumieć, w jaki sposób można łączyć stringi z dowolną zmienną.
Najczęstsze podejścia do adaptacji stringa to zastosowanie:
- Pojedynczych cudzysłowów z konkatenacją
- Podwójnych cudzysłowów ze zmienną w stringu
- Funkcji sprintf (vsprintf)
- Składni heredoc (newdoc)
Skoncentrujemy się na każdej opcji – jej mocnych i słabych stronach.
Najpierw trochę teorii.
Teoria
#1 Pojedyncze cudzysłowy z konkatenacją
String w pojedynczym cudzysłowie nie zawiera interpretowanych zmiennych. W przeciwieństwie do pozostałych opcji, zmienne w stringu w pojedynczym cudzysłowie nie zostaną rozwinięte przy wystąpieniu. Oznacza to, że trzeba zastosować konkatenację.
'Variable is' . $var;
#2 Podwójne cudzysłowy ze zmienną w stringu
W przypadku podwójnych cudzysłowów kompilator PHP rozwinie każdą zmienną wewnątrz stringu w sposób następujący
"Variable is {$var}";
#3 Funkcja sprintf (vsprintf)
Sprintf to najpotężniejsza i najbardziej zaawansowana funkcja formatowania stringów PHP, oferująca wiele opcji kontrolowania wyniku końcowego. Pozwala formatować stringi zgodnie ze specjalnym językiem tworzenia szablonów i wydaje się być znacznie potężniejszym sposobem wstawiania do nich zmiennych, jednak z najwyższym kosztem czasu przetwarzania.
Funkcja sprintf przyjmuje szablon stringa jako pierwszy argument. Może po niej następować nieskończona liczba argumentów - sprintf zamieni symbole zastępcze w stringu zgodnie z argumentami przekazanymi przez Ciebie do funkcji.
sprintf('Variable is %s', $var);
#4 heredoc (i nowdoc)
Here Document jest definiowany jako sekcja pliku kodu źródłowego, która jest traktowana tak, jakby była oddzielnym plikiem.
Ulepszona składnia (począwszy od PHP 7.3) pozwala na stosowanie heredoc w bardziej czytelny i przejrzystszy oraz mniej podatny na błędy sposób. Heredoc pozwala zrobić coś, co jest trudniejsze do uczynienia w cudzysłowie. Pozwala zdefiniować blok tekstu jako literał.
PHP domyślnie przetwarza heredoc tak, jakby był to string w podwójnych cudzysłowach, a nowdoc tak, jakby był to string w pojedynczych cudzysłowach. Aby użyć nowdoc, wystarczy umieścić identyfikator otwierający w pojedynczych cudzysłowach.
Składnia heredoc:
$str = <<<STR
Variable is $var
STR;
Składnia newdoc:
$str = <<<'STR'
Variable is $var
STR;
Praktyka
Przetestujmy każde podejście na kilku przykładach.
Spójrz na następujący tekst:
Knock knock, "LANG" has you. Wake up NAME, ACTION
#1 konkatenacja
$str = 'Knock knock, "' . $lang . '" has you. Wake up ' . $name . ', ' . $action;
Mamy tu do czynienia z ogromną liczbą cudzysłowów, konkatenacji i dodatkowych spacji.
#2 Zmienna w łańcuchu
$str = "Knock knock, \"{$lang}\" has you. Wake up {$name}, {$action}";
Wygląda znacznie lepiej. Po prostu pamiętaj o poprzedzaniu zmiennych i znaków specjalnych znakami (w tym przypadku – “).
#3 Sprintf
$str = sprintf('Knock knock, "%s" has you. Wake up %s, %s', $lang, $name, $action);
Też wygląda całkiem czytelnie, bez konieczności stosowania znaków ucieczki.
Opcjonalnie możemy przekazać zmienne jako tablicę.
$vars = [
'PHP',
'Developer',
'time to code'
];
$str = sprintf('Knock knock, "%s" has you. Wake up %s, %s', ...$vars);
Wygląda to tak samo przy zastosowaniu funkcji vsprintf.
$str = vsprintf('Knock knock, "%s" has you. Wake up %s, %s', $vars);
#4 Heredoc
$str = <<<STR
Knock knock, "$lang" has you. Wake up $name, $action
STR;
Bez znaków ucieczki, bez konkatenacji lub dodatkowych cudzysłowów oraz bez potrzeby wywoływania funkcji, a jedynie 3 linie prostego stringa.
Powiedzmy, że chcemy dodać trochę oznakowania ze zmiennymi dynamicznymi:
<div class="container">
<p style="font-size:SIZEpx;line-height:SIZEpx;">
<span class="first-item" style="color:red">TEXT1</span>
<span class="last-item" style="color:black">TEXT2/span>
</p>
</div>
W tym przypadku zalecamy zastosowanie heredoc:
<<<TEXT
<div class="container">
<p style="font-size:{$size}px;line-height:{$size}px;">
<span class="first-item" style="color:red">$text1</span>
<span class="last-item" style="color:black">$text2</span>
</p>
</div>
TEXT;
Wnętrze stringa wygląda podobnie, ale potrzebujemy trochę więcej znaków ucieczki – „\” oraz {}
"<div class=\"container\">
<p style=\"font-size:{$size}px;line-height:{$size}px;\">
<span class=\"first-item\" style=\"color:red\">{$text1}</span>
<span class=\"last-item\" style=\"color:black\">{$text2}</span>
</p>
</div>";
Konkatenacja wygląda mniej czytelnie, ale dzięki formatowaniu nie irytuje. Jednak nie to jest tutaj głównym problemem:
'<div class="container">
<p style="font-size:' . $size . 'px;line-height:' . $size . 'px;">
<span class="first-item" style="color:red">' . $text1 . '</span>
<span class="last-item" style="color:black">' . $text2 . '</span>
</p></div>';
Wyobraź sobie, że chcesz dodać nową zmienną dynamiczną do tagu <p> w składni heredoc
display: $display;
albo w podwójnym cudzysłowie
display: {$display};
Podczas gdy w przypadku pojedynczego cudzysłowu musimy znaleźć właściwe miejsce przed dodaniem kolejnych konkatenacji i cudzysłowów, bardzo łatwo tu o pomyłkę.
display:' . $display . ';">' . '
A w przypadku sprintif:
sprintf('<div class="container">
<p style="font-size:%spx;line-height:%spx;">
<span class="first-item" style="color:red">%s</span>
<span class="last-item" style="color:black">%s</span>
</p>
</div>', $size, $size, $text1, $text2);
Przekazanie wieloliniowego stringa nie wygląda dobrze, chyba że chcesz najpierw przypisać go do jakiejś zmiennej.
Pamiętaj również, aby utrzymywać niską liczbę zmiennych symboli zastępczych. W przeciwnym razie trudno będzie Ci się odnaleźć pośród ponad 10 parametrów, próbując dodać nowy %s, znaleźć pozycję na liście parametrów i dodać ją do wywołania sprintf.
W powyższym przykładzie, jeśli chcemy dodać nową zmienną
display: %s;
zaraz po wysokości wiersza musimy ustalić, że jest to trzeci parametr i podać go jako argument między $size a $text1. Czyż nie brzmi to dobrze.
W takim przypadku możesz spróbować pobawić się uporządkowanymi symbolami zastępczymi, takimi jak %1$s, %2$s. Poza tym, te symbole zastępcze można powtarzać bez dodawania kolejnych argumentów w kodzie.
sprintf('<div class="container">
<p style="font-size:%1$spx;line-height:%1$spx;display:%4$s">
<span class="first-item" style="color:red">%2$s</span>
<span class="last-item" style="color:black">%3$s</span>
</p>
</div>', $size, $text1, $text2, $display);
Porównanie wydajności
Wstępna konfiguracja naszego testu:
- PHP 7.3
- Liczba iteracji – 5 000 000
- Prostym przypadkiem jest Knock knock, $lang has you
- Skomplikowanym jest Knock knock, "$lang" has you. Wake up $name, $action
Wyniki:
Pojedynczy cudzysłów | Podwójny cudzysłów | SPRINTF | SPRINTF (ALT) | HEREDOC | |
---|---|---|---|---|---|
prosty przypadek, ms |
275 |
259 | 533 | 572 | 260 |
skomplikowany przypadek, ms | 685 | 479 | 935 | 888 | 487 |
MIT: Używanie “ jest szybsze niż ‘. PHP nie będzie stosował dodatkowego przetwarzania do interpretacji zawartości pojedynczego cudzysłowu, podczas gdy przy podwójnych cudzysłowach PHP musi przeanalizować i sprawdzić, czy są jakieś zmienne.
Obecnie argument, że jedna z tych opcji jest lepsza od drugiej, jest trudny do obronienia, chyba że zaczniesz łączyć string ze zmiennymi – wtedy metoda podwójnych cudzysłowów zdecydowanie wygrywa.
Podsumowanie
Uważamy opcję ze zmienną wewnątrz stringa za najbardziej użyteczną metodę łączenia stringów i zmiennych. Jednak ogólnie zaleca się stosowanie każdej z tych metod dla różnorodnych scenariuszy.
Oto zalecenia dla każdego z tych podejść:
- Zwykle najlepiej używać pojedynczych cudzysłowów, chyba że musisz użyć zmiennych wewnątrz łańcucha. W przeciwnym razie zrobi się bałagan, rosnący wraz ze złożonością łańcuchów, ponieważ będziesz musiał radzić sobie z ogromną liczbą cudzysłowów, konkatenacji i dodatkowych spacji. To samo dzieje się z wydajnością – łańcuch z wyższą liczbą konkatenacji jest mniej wydajny w porównaniu z opcjami #2 i #3.
- Stringi w podwójnych cudzysłowach wyglądają lepiej, ponieważ nie trzeba ich rozdzielać za każdym razem, gdy należy wstawić zmienną (tak jak w przypadku stringów w pojedynczych cudzysłowach). W rezultacie łatwiej jest zarówno pisać, jak i czytać takie stringi. Jest to również jedno z najszybszych podejść, szczególnie w trudnych scenariuszach.
- Heredoc zachowuje się jak string w podwójnym cudzysłowie, co również może być odpowiednią dla ciebie opcją. Składnie heredoc i nowdoc są bardzo przydatne, gdy chcemy zdefiniować łańcuch wielowierszowy. Wydajność jest taka sama jak w opcji #2.
- Najbardziej elastyczną i potężną opcją jest sprintf, ale jest także opcją najwolniejszą. Możesz jej używać, ale raczej dla krótszych stringów, ponieważ po 4-5 zmiennych stosowanie tego rozwiązania staje się dość trudne. Pamiętaj, że możesz zrobić znacznie więcej niż tylko wstawiać wartości zmiennych w stringi, na przykład określić format liczb (szesnastkowy, dziesiętny, ósemkowy), liczbę miejsc po przecinku, odstępy i wiele innych.
W Droptica od ponad dzisięciu lat realizujemy usługi PHP development. Niniejszy tekst jest rezultatem zdobytego doświadczenia. Jeśli więc masz projekt, który wymaga eksperckiej wiedzy z zakresu PHP, zapraszam do kontaktu z nami.