Blade: zmienne w widoku i bezpieczne wyświetlanie HTML-a

Blade: zmienne w widoku i bezpieczne wyświetlanie HTML-a
Szybka odpowiedź

W widoku Blade zadeklarujesz zmienną przez @php $nom = '…'; @endphp albo przekażesz ją z kontrolera przez view('vue', compact('nom')). Do wyświetlenia: {{ $nom }} escapuje HTML, a {!! $nom !!} wstawia go bez zmian. Tej drugiej formy używaj wyłącznie do zaufanej treści: to właśnie tam powstają luki XSS.

W widoku Blade wracają bez przerwy dwie potrzeby: zadeklarować w nim zmienną i zdecydować, czy jej treść ma być escapowana, czy wstawiona bez zmian. To drugie nie jest szczegółem wygody, tylko miejscem, w którym wprowadza się lukę XSS.

Opublikowane w ścieżce Programowanie webowe. Ten przewodnik obejmuje jedno i drugie: jak doprowadzić zmienną do widoku, jak zadeklarować ją na miejscu i jak świadomie wybrać między {{ }} a {!! !!}. Przykłady wykonane na Laravelu 13.30.1 z PHP 8.4.25.

Przekazanie zmiennej z kontrolera

To normalna droga i ta, którą warto wybierać: widok wyświetla, kontroler decyduje. Drugi argument view() to tablica asocjacyjna, w której każdy klucz staje się zmienną.

app/Http/Controllers/DashboardController.php
return view('dashboard', ['name' => 'Gekko']);
resources/views/dashboard.blade.php
<p>Bienvenue {{ $name }} !</p>

O wyświetlaniu daty pochodzącej z modelu przeczytasz w tekście o obsłudze dat z Carbonem.

Przy kilku wartościach, które siedzą już w zmiennych PHP, compact() oszczędza powtarzania nazw:

php
$name    = 'Gekko';
$reseau  = 'gekkode';

return view('dashboard', compact('name', 'reseau'));

with() pozwala dorzucić jedną po drodze, co jest wygodne, kiedy wartość liczy się w ostatniej chwili:

php
return view('dashboard', compact('name'))->with('extra', 'valeur ajoutée');

Jeśli zmiennej może zabraknąć, daj jej w widoku wartość zapasową, zamiast dokładać warunek:

php
{{ $inconnu ?? 'valeur par défaut' }}

Deklaracja zmiennej bezpośrednio w widoku

Kiedy wartość służy wyłącznie do wyświetlenia, dyrektywa @php pozwala zadeklarować ją na miejscu:

resources/views/dashboard.blade.php
@php
    $nom = 'Gekko';
    $classe = $task->active ? 'is-active' : 'is-done';
@endphp

<p class="{{ $classe }}">Bonjour {{ $nom }}</p>

Klasyczne znaczniki PHP też działają, ale @php jest czytelniejsze i spójne z resztą szablonu.

Dwie dyrektywy obsługują sąsiednie przypadki bez udziału zmiennej. @class buduje atrybut klasy na podstawie warunków:

php
<div @class(['actif' => $task->active, 'masque' => $task->hidden])></div>

<!-- rendu quand active vaut true et hidden false -->
<div class="actif"></div>

A @once gwarantuje, że blok wyrenderuje się tylko raz, nawet jeśli widok jest dołączony na stronie kilka razy:

php
@once
    <script src="/js/carte.js" defer></script>
@endonce
Gdzie kończy się rozsądek

@php nadaje się do wartości czysto wyświetlanej. Gdy tylko w grę wchodzi zapytanie do bazy, wyliczenie biznesowe albo pętla przekształcająca dane, kod należy do kontrolera, komponentu Blade albo akcesora modelu. Widok, który odpytuje bazę, generuje zapytania N+1 niewidoczne z poziomu kontrolera.

Wyświetlanie HTML-a: {{ }} kontra {!! !!}

Blade kompiluje {{ $variable }} do wywołania funkcji e(), która escapuje znaki specjalne. Widać to w skompilowanym widoku, w storage/framework/views:

php
<?php echo e($nom); ?>

Skutek: jeśli zmienna zawiera znaczniki, wyświetlą się dosłownie, zamiast zostać zinterpretowane.

php
@php $html = '<strong>gras</strong>'; @endphp

{{ $html }}     {{-- affiche : &lt;strong&gt;gras&lt;/strong&gt; --}}
{!! $html !!}   {{-- affiche : gras, en gras --}}

Składnia {!! !!} wstawia wartość bez żadnego przekształcenia. Tego właśnie trzeba, żeby wyświetlić treść artykułu napisaną w HTML-u, i to samo jest furtką do wstrzyknięcia skryptu, kiedy wartość pochodzi od użytkownika.

Zasada, której się nie obchodzi

{!! !!} stosuj wyłącznie do HTML-a, nad którego pochodzeniem panujesz albo który został wcześniej wyczyszczony. Przy wartości <script>alert(1)</script> {{ }} wyświetla tekst, a {!! !!} wykonuje skrypt w przeglądarce odwiedzającego. Oba zachowania zostały sprawdzone.

Treść przychodzącą z formularza, który wcześniej zabezpieczysz przed automatycznymi wysyłkami, wyczyść przed wyświetleniem. Biblioteka w rodzaju HTML Purifier stosuje białą listę znaczników, w jej braku przechowuj Markdown i konwertuj go przy renderowaniu, co zostawia ci kontrolę nad wygenerowanymi znacznikami.

Przydatne przypadki szczególne

Do przekazania danych do JavaScriptu @json tworzy poprawny i escapowany literał:

php
<script>
    const config = @json(['id' => $task->id, 'titre' => $task->title]);
</script>

Żeby wyświetlić klamry, których Blade nie ma interpretować, na przykład w szablonie przeznaczonym dla frameworka JavaScript, @verbatim neutralizuje cały blok:

php
@verbatim
    <div id="app">{{ message }}</div>
@endverbatim

Przy pojedynczym wyrażeniu wystarczy poprzedzić je małpą: @{{ message }}.

Nie wywołuj e() w podwójnych klamrach

{{ e($valeur) }} escapuje dwa razy: <b> zamienia się w &amp;lt;b&amp;gt; i wyświetla się dosłownie na ekranie. {{ }} robi to już samo. Funkcja e() przydaje się tylko poza Blade albo wewnątrz {!! !!} na konkretnym fragmencie.

Częste błędy

{!! !!} na wartości od użytkownika Ciąg <script>alert(1)</script> naprawdę wykona się w przeglądarce odwiedzającego. Wyczyść HTML wcześniej albo przechowuj Markdown.
{{ e($valeur) }} escapuje dwa razy Podwójne klamry same wywołują już e(). W efekcie na ekranie widać encje zamiast tekstu.
Zapytanie do bazy w @php Widok wywołuje zapytania, których kontroler nie widzi, co daje niewidoczne N+1. Przenieś logikę do kontrolera albo do komponentu.
Brak wartości zapasowej przy zmiennej opcjonalnej {{ $var ?? 'défaut' }} zapobiega błędowi, gdy kontroler nie przekaże jej w każdym przypadku.

BladeLaravelPHP

Damien Flandrin Web developer od 2010 roku, twórca Gekkode i Email Impact. Każdy artykuł jest sprawdzany na prawdziwym projekcie przed publikacją. Kontakt
Newsletter

Nowe testy, poradniki i projekty — e-mailem.

Powtarzalne testy, wersjonowany kod, datowane wyniki. Nigdy spamu.