string

Łą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:

  1. Pojedynczych cudzysłowów z konkatenacją
  2. Podwójnych cudzysłowów ze zmienną w stringu
  3. Funkcji sprintf (vsprintf)
  4. 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

wyniki

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ść:

  1. 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.
  2. 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.
  3. 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.
  4. 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.

3. Najlepsze praktyki zespołów programistycznych