Migracja z Dompdf do NextPDF
W skrócie
Dział zatytułowany „W skrócie”Ten przewodnik pomaga przenieść kod oparty na Dompdf, który generuje pliki w formacie Portable Document Format (PDF) z języka Hypertext Markup Language (HTML), na potok Html silnika NextPDF. Większość miejsc wywołań przekłada się mechanicznie, ponieważ obie biblioteki realizują ten sam ogólny przepływ: wczytanie HTML, renderowanie i wyemitowanie pliku PDF. Najważniejsza praca dotyczy mapy opcji oraz różnic w obsłudze Cascading Style Sheets (CSS). NextPDF i Dompdf to niezależne silniki, więc układ utworzony przez Dompdf jest zgodny z wynikiem silnika NextPDF, a nie identyczny z nim bajt po bajcie. Ten przewodnik obejmuje mapowanie czasowników, mapowanie kluczy opcji, różnice w zachowaniu oraz bezpieczną sekwencję migracji.
Obsługa danej funkcji HTML/CSS przez NextPDF nie gwarantuje, że dokument Dompdf zostanie odtworzony piksel po pikselu. Źródłem rozstrzygającym dla funkcji zweryfikowanych jest Macierz obsługi CSS. Ten przewodnik opisuje zachowanie, ale nie deklaruje równoważności wizualnej.
Instalacja
Dział zatytułowany „Instalacja”composer require nextpdf/core:^3Zachowaj zainstalowany pakiet dompdf/dompdf na czas przejścia. Opisana niżej bezpieczna sekwencja migracji uruchamia oba silniki obok siebie, dopóki każde miejsce wywołania nie zostanie przełączone. Usuń pakiet po zakończeniu przełączenia.
Przegląd koncepcyjny
Dział zatytułowany „Przegląd koncepcyjny”Obiekt Dompdf w Dompdf to pojedyncza fasada, która zarządza modelem Document Object Model (DOM), arkuszem stylów, drzewem ramek oraz kanwą. NextPDF rozdziela te zadania: NextPDF\Core\Document zarządza modelem strony i wyjściem, a writeHtml() steruje potokiem HTML. Nie ma osobnej, dwuetapowej fazy „renderowanie, potem wyjście”. writeHtml() rozkłada treść w miarę jej zapisywania, a dokument emituje się za pomocą save(), output() lub getPdfData().
NextPDF zapisuje treść strony przez malowanie strumienia treści zgodnie z ISO 32000-2 (ISO 32000-2 §8, iso32000_2_sec8#x1.x3.p14). Geometria strony sterowana opcją rozmiaru papieru odwzorowuje się na obiekt strony MediaBox (ISO 32000-2 §7, iso32000_2_sec7#x1.x104.p10). Są to podstawy działania silnika, wspólne dla każdego zgodnego ze specyfikacją modułu zapisującego. Algorytm układu, który przekształca CSS w tę treść, jest własnym algorytmem silnika NextPDF i różni się od algorytmu Dompdf; zobacz Różnice w zachowaniu.
Powierzchnia API
Dział zatytułowany „Powierzchnia API”Interfejs programowania aplikacji (API) potoku Html silnika NextPDF jest udokumentowany w dokumentacji referencyjnej modułu Html, generowanej automatycznie z PHPDoc. Punkty wejścia używane poniżej to Document::createStandalone(), Document::writeHtml(string $html): static, Document::writeHtmlCell(...), Document::output(?string, OutputDestination), Document::save(string $path): void, Document::getPdfData(): string oraz obiekt wartości NextPDF\Core\Config (pageSize, margins, fontsDirectory).
Mapowanie czasowników API
Dział zatytułowany „Mapowanie czasowników API”Nazwy publicznego API Dompdf podane poniżej zostały potwierdzone w publicznym repozytorium upstream (dompdf/dompdf, master); zobacz dołączony do repozytorium plik pochodzenia _source-sidecar-upstream-api.md. Nie przytoczono żadnego tekstu dokumentacji upstream.
| Dompdf | NextPDF | Uwagi |
|---|---|---|
new Dompdf($options) | Document::createStandalone($config) | Dompdf przyjmuje obiekt Options; NextPDF przyjmuje obiekt NextPDF\Core\Config. W długo działających procesach roboczych użyj DocumentFactory zamiast createStandalone(). |
$dompdf->loadHtml($html, $encoding) | $doc->writeHtml($html) | NextPDF traktuje dane wejściowe jako UTF-8. Przekonwertuj dane wejściowe spoza UTF-8 przed wywołaniem, zamiast przekazywać argument kodowania. |
$dompdf->loadHtmlFile($file) | $doc->writeHtml(file_get_contents($file)) | NextPDF nie ma wariantu wczytującego plik. Odczytaj plik samodzielnie, aby polityka zasobów pozostała w Twoim kodzie. |
$dompdf->setPaper($size, $orientation) | ConfigpageSize (obiekt wartości PageSize) | Zobacz mapę opcji. |
$dompdf->render() | (niejawnie) | NextPDF rozkłada układ podczas writeHtml(); nie ma osobnej fazy renderowania. Usuń wywołanie render(). |
$dompdf->output() | $doc->getPdfData() | Zwraca bajty pliku PDF. |
$dompdf->stream($name, $opts) | $doc->output($name, OutputDestination::Download) | NextPDF wybiera miejsce docelowe za pomocą wyliczenia OutputDestination. |
$dompdf->setBasePath($p) / setProtocol() / setBaseHost() | (rozstrzyganie zasobów różni się) | NextPDF rozstrzyga zasoby względne względem zestawu roboczego dokumentu, a nie bazowej trójki path/protocol; zobacz Różnice w zachowaniu. |
$dompdf->addInfo($label, $value) | $doc->setTitle() / setAuthor() / API metadanych | Dowolne pary informacji z Dompdf odwzorowują się na typowane settery metadanych (ISO 32000-2 §14 informacje o dokumencie, iso32000_2_sec14#x1.x5.p5). |
$dompdf->setHttpContext($ctx) | (brak odpowiednika) | NextPDF nie pobiera zasobów zdalnych przez kontekst strumienia; zobacz Nieobsługiwane / brak bezpośredniego odpowiednika. |
Przykład kodu — Szybki start
Dział zatytułowany „Przykład kodu — Szybki start”<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
// Dompdf:// $dompdf = new Dompdf();// $dompdf->loadHtml('<h1>Invoice</h1>');// $dompdf->setPaper('A4', 'portrait');// $dompdf->render();// file_put_contents('out.pdf', $dompdf->output());
// NextPDF — the createStandalone() default page size is A4 portrait:$doc = Document::createStandalone();$doc->setTitle('Invoice');$doc->addPage();$doc->writeHtml('<h1>Invoice</h1>');$doc->save(__DIR__ . '/out.pdf');
echo "Wrote out.pdf\n";Przykład kodu — Produkcja
Dział zatytułowany „Przykład kodu — Produkcja”Poniższy przykład odzwierciedla examples/08-html-basic.php, uruchamialne zaplecze tego przewodnika, z jawnie ustawionym rozmiarem papieru innym niż domyślny oraz marginesami. Odpowiada to wywołaniu Dompdf setPaper() wraz z konfiguracją marginesów Options.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Contracts\OutputDestination;use NextPDF\Core\Config;use NextPDF\Core\Document;use NextPDF\ValueObjects\Margin;use NextPDF\ValueObjects\PageSize;
// Equivalent of: $dompdf->setPaper('letter','portrait') + margin options.// US Letter portrait = 612 x 792 pt.// Margin constructor order is (top, right, bottom, left) — all 0.5in here.$config = new Config( pageSize: new PageSize(612.0, 792.0, 'Letter'), margins: new Margin(36.0, 36.0, 36.0, 36.0), // top,right,bottom,left; 0.5in in points);
$doc = Document::createStandalone($config);$doc->setTitle('Quarterly Report');$doc->setAuthor('Finance');$doc->addPage();
$html = <<<'HTML'<h1 style="color:#1E3A8A;">Quarterly Report</h1><p>This report renders through the NextPDF Html pipeline. The CSS subset thatis <strong>Verified</strong> for production is the support-matrix authority,not this page.</p><table border="1"> <tr><th>Region</th><th>Total</th></tr> <tr><td>EMEA</td><td>1,204</td></tr></table>HTML;
$doc->writeHtml($html);
// Equivalent of $dompdf->stream('report.pdf'):$doc->output('report.pdf', OutputDestination::Download);Przypadki brzegowe i pułapki
Dział zatytułowany „Przypadki brzegowe i pułapki”- Brak dwufazowego renderowania. Kod Dompdf, który sprawdza stan między
render()aoutput(), taki jak odczyt liczby stron, nie ma bezpośredniego odpowiednika w NextPDF na tej samej granicy. Zamiast tego odpytaj dokument powriteHtml(). - Kodowanie. NextPDF pomija parametr
$encodingz Dompdf. Przekonwertuj dane wejściowe na UTF-8 przedwriteHtml(). Przekazanie bajtów Latin-1 powoduje mojibake, a nie błąd. - Pozostawione
render(). Pozostawione wywołanie w stylu$dompdf->render()nie odpowiada żadnej metodzie w NextPDF i kończy się krytycznym błędem „undefined method”. Usuń je podczas przełączenia; nie zastępuj go zaślepką. - Wbudowany PHP.
enable_phpw Dompdf wykonuje<script type="text/php">. NextPDF z założenia nie wykonuje PHP wewnątrz dokumentu, ponieważ byłaby to powierzchnia ataku przez wstrzyknięcie. Przenieś tę logikę do swojego kodu PHP przedwriteHtml(). - Rozstrzyganie zasobów względnych. Dompdf rozstrzyga
<img src>względem trójki bazowej path/protocol/host. NextPDF rozstrzyga względem zestawu roboczego dokumentu. Podczas migracji przekazuj ścieżki bezwzględne lub wstępnie rozstrzygnięte identyfikatory data URI, aby wyeliminować tę zmienną.
Wydajność
Dział zatytułowany „Wydajność”writeHtml() rozkłada układ w jednym przebiegu strumieniowym, jak opisano w rekordzie decyzji architektonicznej ADR-001. Po ułożeniu dokumentu nie jest zachowywany żaden pośredni obiekt drzewa ramek, więc szczytowe zużycie pamięci zależy od rozmiaru dokumentu, a nie od liczby węzłów DOM. Budżet wydajności przykładu w tym przewodniku to wall_ms: 2000, peak_mb: 128. W przypadku dużych dokumentów dziel HTML na granicach addPage(), zamiast budować jeden wielomegabajtowy ciąg znaków.
Uwagi dotyczące bezpieczeństwa
Dział zatytułowany „Uwagi dotyczące bezpieczeństwa”- Brak pobierania zdalnego przez kontekst strumienia. NextPDF nie implementuje ścieżki zdalnego pobierania
setHttpContext()/enable_remotez Dompdf. Rozstrzygnij i zweryfikuj zasoby zdalne w swojej aplikacji, a następnie przekaż bajty lub identyfikatory data URI. Eliminuje to powierzchnię ataku server-side request forgery (SSRF), którą wprowadzaenable_remote. - Brak wykonywania kodu wewnątrz dokumentu. Brak odpowiednika
enable_phpto celowe utwardzenie, a nie luka. - Metadane dokumentu ustawiane przez typowane settery są zapisywane do słownika informacji ISO 32000-2 §14 / Extensible Metadata Platform (XMP) (
iso32000_2_sec14#x1.x5.p5). Nie umieszczaj tam danych poufnych.
Zgodność
Dział zatytułowany „Zgodność”| Stwierdzenie | Specyfikacja | Klauzula | reference_id |
|---|---|---|---|
| Treść strony jest malowaniem strumienia treści w modelu opaque/transparent. | ISO 32000-2 | §8 | |
| Rozmiar papieru odwzorowuje się na prostokąt graniczny obiektu strony. | ISO 32000-2 | §7 | |
| Czcionki HTML są zapisywane jako programy czcionek embedded/subset. | ISO 32000-2 | §9 | |
| Przetwarzanie białych znaków i łamania wierszy jest specyficzne dla silnika. | CSS Text 3 | §6.5 |
NextPDF tworzy treść zgodną z ISO 32000-2. Nie deklaruje, że dokument zmigrowany z Dompdf jest identyczny wizualnie. Ponownie sprawdzaj wynik za każdym razem, gdy zmieniasz silnik renderujący.
Kontekst komercyjny
Dział zatytułowany „Kontekst komercyjny”Nie dotyczy. Rdzeń obejmuje tę ścieżkę migracji z HTML na PDF.
Zobacz też
Dział zatytułowany „Zobacz też”Szczegóły migracji (sekcje wymagane w R6)
Dział zatytułowany „Szczegóły migracji (sekcje wymagane w R6)”Dla kogo jest ten przewodnik
Dział zatytułowany „Dla kogo jest ten przewodnik”Skorzystaj z niego, jeśli Twój zespół używa dompdf/dompdf do konwersji HTML na PDF po stronie serwera i chce przejść na silnik NextPDF. Jeśli wywołujesz tylko loadHtml / setPaper / render / output, mapowanie czasowników obejmuje całą używaną powierzchnię API.
Zakres obejmuje: czasowniki fasady Dompdf, klucze Options, oczekiwania dotyczące zgodności funkcji CSS, rozstrzyganie zasobów oraz metadane. Poza zakresem są wewnętrzne obiekty FrameTree/Canvas/Stylesheet w Dompdf. NextPDF nie ma publicznych odpowiedników, więc nie migruj kodu, który do nich sięga; zastąp go publicznym API.
Mapa zgodności
Dział zatytułowany „Mapa zgodności”Pokrycie oznacza zgodność behawioralną, a nie nakładkę typu drop-in. NextPDF nie ma nakładki klasy Dompdf (inaczej niż ścieżka TCPDF; zobacz /migration/tcpdf-compat/). Przepisz każde miejsce wywołania przy użyciu mapowania czasowników. Zweryfikowane wiersze macierzy obsługi CSS w pełni określają oczekiwania dotyczące funkcji CSS. Ten przewodnik nie powtarza statusu poszczególnych właściwości.
Mapa opcji i konfiguracji
Dział zatytułowany „Mapa opcji i konfiguracji”| Opcja Dompdf (klucz / setter) | NextPDF | Uwagi |
|---|---|---|
default_paper_size / setDefaultPaperSize() ; setPaper($size,...) | Config->pageSize (obiekt wartości PageSize) | Nazwane rozmiary stają się jawnymi wymiarami w punktach. new PageSize(595.276, 841.890, 'A4') to domyślna wartość createStandalone(). |
default_paper_orientation / setDefaultPaperOrientation() | zamień width/height w PageSize | NextPDF nie ma flagi orientacji. Strona w orientacji poziomej to PageSize z szerokością > wysokością. |
dpi / setDpi() | (nie jest globalnym parametrem) | NextPDF działa w punktach PDF (1/72 cala). Skalowanie obrazów odbywa się dla każdego obrazu z osobna, a nie jako mnożnik dokumentu dots per inch (DPI). Przelicz stałe rozmiary w pikselach na punkty. |
enable_remote / setIsRemoteEnabled() | (brak odpowiednika — celowo) | Rozstrzygnij zasoby zdalne w swoim kodzie; zobacz Uwagi dotyczące bezpieczeństwa. |
enable_html5_parser / setIsHtml5ParserEnabled() | (zawsze parsuje HTML) | Brak przełącznika; parser jest częścią potoku. |
enable_php / setIsPhpEnabled() | (brak odpowiednika — celowo) | PHP wewnątrz dokumentu nie jest obsługiwany. Przenieś logikę poza szablon. |
font_dir / setFontDir() | Config->fontsDirectory | Pojedynczy ciąg znaków katalogu czcionek. |
chroot | (rozstrzygnij w aplikacji) | NextPDF nie przyjmuje opcji izolacji systemu plików. Zweryfikuj ścieżki przed przekazaniem bajtów. |
default_font / setDefaultFont() | CSS font-family / zarejestrowana czcionka | Ustaw wartość domyślną w bazowym arkuszu stylów lub przy rejestracji czcionki, a nie w opcji globalnej. |
enable_font_subsetting / setIsFontSubsettingEnabled() | (zawsze tworzy podzbiory) | NextPDF zawsze tworzy podzbiory osadzonych czcionek (ISO 32000-2 §9, iso32000_2_sec9#x1.x45.p7). Nie ma opcji „off”; ścieżka Dompdf z wyłączoną flagą nie ma odpowiednika i nie jest potrzebna. |
Różnice w zachowaniu
Dział zatytułowany „Różnice w zachowaniu”- Silnik układu. Dompdf i NextPDF używają niezależnych implementacji układu CSS. Zwijanie białych znaków oraz łamanie wierszy są określone specyfikacją, ale pozostają zależne od silnika (CSS Text 3 §6.5,
css_text_3#x1.x6.x5.p20). Przy gęstym tekście spodziewaj się różnic w zawijaniu wierszy i podziale na strony. Po migracji ponownie ustal punkt odniesienia dla różnic wizualnych. - Granica renderowania. Brak dwufazowej granicy
render()/output()(zobacz Przypadki brzegowe). - Rozstrzyganie zasobów. Obsługa ścieżki bazowej/protokołu/hosta różni się od zestawu roboczego dokumentu.
- Model DPI. Punkty PDF różnią się od mnożnika DPI w Dompdf.
- Metadane. Dowolne pary
addInfo()różnią się od typowanych setterów (ISO 32000-2 §14,iso32000_2_sec14#x1.x5.p5).
To udokumentowane różnice w zachowaniu, a nie wady któregokolwiek z silników.
Nieobsługiwane / brak bezpośredniego odpowiednika
Dział zatytułowany „Nieobsługiwane / brak bezpośredniego odpowiednika”enable_php(PHP wewnątrz dokumentu) — celowo nieobecne.setHttpContext()/enable_remotepobieranie zdalne — celowo nieobecne.- Publiczny dostęp do
FrameTree/Canvas/Stylesheet— brak publicznego odpowiednika. dpijako globalny mnożnik dokumentu — nie jest modelowane.
Kod, który zależy od tych elementów, nie podlega „migracji”. Usuń go albo zapisz ponownie w kodzie aplikacji, korzystając z powyższych wierszy.
Bezpieczna sekwencja migracji
Dział zatytułowany „Bezpieczna sekwencja migracji”- Dodaj
nextpdf/coreobokdompdf/dompdf. Nie usuwaj jeszcze Dompdf. - Wybierz jeden dokument o niskim ryzyku. Przepisz jego miejsce wywołania przy użyciu mapowania czasowników i usuń wywołanie
render(). - Wygeneruj oba pliki PDF z tych samych danych wejściowych i porównaj je wizualnie. Traktuj różnice jako oczekiwane, ponieważ silniki są niezależne, a akceptację oceniaj dla każdego dokumentu z osobna.
- Przerób użycie opcji przy użyciu mapy opcji; przelicz rozmiary pochodzące z DPI na punkty.
- Wstępnie rozstrzygnij zasoby zdalne i względne na ścieżki bezwzględne lub identyfikatory data URI, aby wyeliminować zmienną rozstrzygania.
- Powtarzaj dla każdego dokumentu, od najniższego do najwyższego ryzyka. Zachowaj oba silniki zainstalowane, dopóki ostatnie miejsce wywołania nie zostanie przełączone.
- Usuń
dompdf/dompdfzcomposer.jsondopiero po ostatnim przełączeniu.
Testowanie migracji
Dział zatytułowany „Testowanie migracji”- Wykonaj zrzut wyjścia Dompdf dla reprezentatywnych dokumentów przed zmianą kodu. Używaj referencyjnych danych wejściowych, a nie referencyjnych bajtów, ponieważ bajty będą się różnić.
- Dla każdego zmigrowanego dokumentu przepuść wynik NextPDF przez własną procedurę akceptacji, taką jak różnica wizualna lub asercje wyodrębniania tekstu. Własne zachowanie potoku HTML silnika NextPDF w
examples/08-html-basic.phpjest objęte przeztests/oraz pakiet testów Html w rdzeniu. Akceptacja migracji zależy od dokumentu i to Ty odpowiadasz za jej potwierdzenie. - Dodaj test regresji dla każdego zmigrowanego dokumentu, aby wychwycić zmiany po przyszłej aktualizacji silnika.
Dowody / identyfikowalność
Dział zatytułowany „Dowody / identyfikowalność”Każde stwierdzenie behawioralne dotyczące NextPDF na tej stronie jest poparte testem w repozytorium, przykładem, sygnaturą źródła lub rekordem decyzji architektonicznej (ADR), a w przypadku właściwości formatu PDF — klauzulami ISO 32000-2 / CSS zakotwiczonymi przez Retrieval-Augmented Generation (RAG) w polu citations: frontmatteru oraz tabelą Zgodność. Zachowanie Dompdf jest stwierdzane wyłącznie jako „niezależny silnik — spodziewaj się udokumentowanych różnic”. Ta strona nie deklaruje żadnej równoważności, o ile nie dowodzi tego artefakt w repozytorium.
| NextPDF stwierdzenie behawioralne | Dowód w repozytorium (ścieżka) |
|---|---|
Domyślna strona createStandalone() to A4 w orientacji pionowej (595.276 × 841.890 pt). | src/Core/Config.php (domyślnie PageSize(595.276, 841.890, 'A4')); tests/Unit/Core/DocumentCreateStandaloneAndConfigWithersEdgeCaseTest.php (createStandaloneWithNullConfigBuildsDocumentWithA4Defaults). |
writeHtml() rozkłada układ w jednym przebiegu strumieniowym; po wykonaniu układu nie zachowuje DOM. | docs/architecture/adr/ADR-001-stream-based-rendering-pipeline.md; src/Core/Concerns/HasTextOutput.php (writeHtml()). |
writeHtml() automatycznie tworzy pierwszą stronę, gdy żadna nie istnieje. | tests/Unit/Core/Concerns/DocumentTextOutputFontSubsettingAndBorderEdgeCaseTest.php (writeHtmlAutoCreatesFirstPageWhenNoPagesExist). |
output() / save() / getPdfData() to czasowniki emisji (brak dwufazowego schematu render/output). | src/Core/Concerns/HasOutput.php (output(), save(), getPdfData()); tests/Unit/Core/Concerns/DocumentOutputDestinationDispatchTest.php. |
Miejsce docelowe wyjścia to wyliczenie NextPDF\Contracts\OutputDestination (Inline/Download/File/String). | src/Contracts/OutputDestination.php; tests/Unit/Core/Concerns/DocumentOutputDestinationDispatchTest.php. |
| Czcionki HTML są zawsze zapisywane jako programy embedded/subset. | tests/Unit/Core/Concerns/DocumentTextOutputFontSubsettingAndBorderEdgeCaseTest.php (recordUsedCharactersAffectsFontSubsetting); ISO 32000-2 §9 (frontmatter citations:). |
Typowane settery metadanych (setTitle/setAuthor) zastępują dowolne addInfo(). | src/Core/Concerns/HasMetadata.php (setTitle(), setAuthor()); tests/Unit/Core/Concerns/DocumentInfoMetadataSetterBaselineTest.php. |
| Kompleksowy potok HTML (uruchamialne zaplecze tego przewodnika). | examples/08-html-basic.php; pakiet testów tests/Unit/Html/ w rdzeniu. |
| Białe znaki / łamanie wierszy są specyficzne dla silnika (różnica układu). | CSS Text 3 §6.5 (frontmater citations: + Zgodność). |
Wycofanie
Dział zatytułowany „Wycofanie”Ponieważ oba pakiety pozostają zainstalowane aż do ostatecznego przełączenia, wycofanie nieprzekonwertowanego miejsca wywołania oznacza przywrócenie tego miejsca do ścieżki Dompdf. Po ostatecznym przełączeniu wycofanie oznacza przywrócenie dompdf/dompdf oraz poprzedniego miejsca wywołania z systemu kontroli wersji. Dotyczy to wyłącznie zmian w kodzie, nie migracji danych.
Rozważania dotyczące wydajności
Dział zatytułowany „Rozważania dotyczące wydajności”Zobacz Wydajność. Model jednoprzebiegowy oznacza, że migracja nie wprowadza kosztu utrzymywania drzewa ramek. Główna zmiana kosztu dla dokumentu wynika z wyprzedzającego rozstrzygania zasobów z kroku 5, które można buforować.
Częste pułapki
Dział zatytułowany „Częste pułapki”- Pozostawienie
render()na miejscu, co powoduje krytyczny błąd niezdefiniowanej metody. - Przekazanie bajtów spoza UTF-8 po usunięciu
$encoding, co powoduje ciche mojibake. - Oczekiwanie wyniku identycznego bajt po bajcie lub piksel po pikselu od niezależnych silników. Ten przewodnik nigdy nie deklaruje rozwiązania typu drop-in ani 100% zgodności.
- Poleganie na szablonach
enable_php, które trzeba zrefaktoryzować. - Traktowanie macierzy obsługi CSS jako informacji pomocniczej. Jest ona źródłem rozstrzygającym dla funkcji zweryfikowanych i określa, czego się spodziewać.