Tworzę motyw WordPress za pomocą silnika szablonów. Chcę, aby mój kod był jak najbardziej zgodny z podstawową funkcjonalnością WP.
Najpierw jakiś kontekst
Moim pierwszym problemem było znalezienie sposobu rozwiązania szablonu, zaczynając od zapytania WP. Rozwiązałem ten przy użyciu mojej biblioteki, Brain \ Hierarchy .
Jeśli chodzi o get_template_part()
inne funkcje ładujące częściowe get_header()
, takie jak get_footer()
i podobne, dość łatwo było napisać opakowanie do częściowej funkcjonalności silnika szablonu.
Problem
Mój problem polega teraz na tym, jak załadować szablon komentarzy.
Funkcja WordPress comments_template()
to ~ 200 linii funkcja, która robi wiele rzeczy, które chcę zrobić również dla maksymalnej kompatybilności rdzenia.
Jednak jak tylko zadzwonię comments_template()
, plik ma postać require
d, jest to pierwszy z następujących elementów:
- plik w stałej
COMMENTS_TEMPLATE
, jeśli jest zdefiniowany comments.php
w folderze motywu, jeśli został znaleziony/theme-compat/comments.php
w WP zawiera folder jako rezerwowy w ostateczności
Krótko mówiąc, nie ma sposobu, aby funkcja nie załadowała pliku PHP, co nie jest dla mnie pożądane, ponieważ muszę renderować moje szablony, a nie po prostu ich używać require
.
Aktualne rozwiązanie
W tej chwili wysyłam pusty comments.php
plik i korzystam z 'comments_template'
haka filtru, aby wiedzieć, który szablon WordPress chce załadować, i użyć funkcji z mojego silnika szablonów, aby załadować szablon.
Coś takiego:
function engineCommentsTemplate($myEngine) {
$toLoad = null; // this will hold the template path
$tmplGetter = function($tmpl) use(&$toLoad) {
$toLoad = $tmpl;
return $tmpl;
};
// late priority to allow filters attached here to do their job
add_filter('comments_template', $tmplGetter, PHP_INT_MAX);
// this will load an empty comments.php file I ship in my theme
comments_template();
remove_filter('comments_template', $tmplGetter, PHP_INT_MAX);
if (is_file($toLoad) && is_readable($toLoad)) {
return $myEngine->render($toLoad);
}
return '';
}
Pytanie
Działa, jest kompatybilny z rdzeniem, ale ... czy istnieje sposób, aby działał bez konieczności wysyłania pustego comments.php
?
Ponieważ mi się nie podoba.
comments_template
filtru lubCOMMENTS_TEMPLATE
stałej w celu dostosowania szablonu. Co nie jest kluczowe, ale, jak powiedziałem, chciałem zachować jak największą zgodność z rdzeniem.Rozwiązanie: Użyj pliku tymczasowego - z unikalną nazwą pliku
Po wielu skokach i pełzaniu w najbrudniejsze zakątki PHP sformułowałem pytanie w następujący sposób:
jak w samym rdzeniu kod
Następnie pytanie zostało rozwiązane szybciej:
i to wszystko. Może lepiej byłoby użyć
wp_upload_dir()
zamiast tego:Inną opcją może być użycie,
get_temp_dir()
które zawijaWP_TEMP_DIR
. Wskazówka: Dziwnie wraca do tego,/tmp/
aby pliki nie były zachowywane między restartami, co/var/tmp/
byłoby. Na końcu można wykonać proste porównanie łańcucha i sprawdzić wartość zwracaną, a następnie naprawić to w razie potrzeby - czego nie ma w tym przypadku:Teraz szybko przetestuj, czy w pliku tymczasowym bez zawartości nie wystąpiły błędy:
I: Brak błędów → działa.
EDYCJA: Jak zauważył @toscho w komentarzach, jest jeszcze lepszy sposób:
Uwaga: Zgodnie z nutą użytkowników na docs php.net , że
sys_get_temp_dir()
różni się zachowanie między systemami. Dlatego wynik usuwa ukośnik końcowy, a następnie dodaje ponownie. Ponieważ podstawowy błąd nr 22267 został naprawiony, teraz również powinien działać na serwerach Win / IIS.Twoja funkcja refaktoryzacji (nie testowana):
Bonus nr 1:
tmpfile()
powróciNULL
. Tak, naprawdę.Bonus nr 2:
file_exists( __DIR__ )
powróciTRUE
. Tak, naprawdę… na wypadek gdybyś zapomniał.^ Prowadzi to do faktycznego błędu w rdzeniu WP.
Aby pomóc innym osobom przechodzącym w trybie eksploratora i znajdującym je (źle dla nieudokumentowanych elementów), szybko podsumuję to, co próbowałem:
Próba 1: plik tymczasowy w pamięci
Pierwszą próbą, jaką podjąłem, było utworzenie strumienia do pliku tymczasowego przy użyciu
php://temp
. Z dokumentów PHP:Kod:
Znalezienie: Nie, nie działa.
Próba 2: użyj pliku tymczasowego
Jest
tmpfile()
, więc dlaczego tego nie użyć ?!Tak, tyle o tym skrócie.
Próba 3: użyj niestandardowego opakowania strumienia
Następnie pomyślałem, że mogę zbudować niestandardowe opakowanie strumienia i zarejestrować je za pomocą
stream_wrapper_register()
. Następnie mógłbym użyć wirtualnego szablonu z tego strumienia, aby oszukać rdzeń w przekonaniu, że mamy plik. Przykładowy kod poniżej (już usunąłem pełną klasę, a historia nie ma wystarczającej liczby kroków…)Ponownie, to wrócił
NULL
nafile_exists()
.Testowane z PHP 5.6.20
źródło
stream_stat()
? Myślę, że to właśniefile_exists()
zadzwoni, aby sprawdzić ... php.net/manual/en/streamwrapper.stream-stat.phptempnam()
. Korzystanie z zadania cron będzie działać, ale jest to dodatkowy narzut ...tempnam( sys_get_temp_dir(), 'comments.php' )
jest napisany raz , możesz ponownie użyć nazwy pliku, a plik jest pusty , więc nie zużywa wielu zasobów. Ponadto kod jest łatwy do zrozumienia. Zdecydowanie najlepsze rozwiązanie, imho.Ponieważ @AlainSchlesser zasugerował, aby podążać tą trasą (a ponieważ niedziałające rzeczy zawsze powodują błąd), ponowiłem próbę zbudowania opakowania strumienia dla plików wirtualnych. Nie mogłem go rozwiązać (czytaj: czytanie wartości zwracanych w dokumentach) na własną rękę, ale rozwiązałem go przy pomocy @HPierce na SO .
Musisz tylko zarejestrować nową klasę jako nowy protokół:
Umożliwia to utworzenie wirtualnego (nieistniejącego) pliku:
Twoja funkcja może zostać przekształcona w:
ponieważ
file_exists()
rdzeń odprawy powracaTRUE
irequire $file
nie zgłasza błędu.Muszę zauważyć, że jestem bardzo szczęśliwy, jak się to potoczyło, ponieważ może być naprawdę pomocne w testach jednostkowych.
źródło