Zamknięcie połączenia i dalsze wykonywanie skryptu w PHP
Witajcie, dzisiaj zaprezentuję wam szalenie przydatny trik, który znajduje zastosowanie zawsze wtedy, kiedy skrypt ma do wykonania jakieś czasochłonne zajęcie a nie chcielibyśmy zbytnio wystawiać na próbę cierpliwość naszych użytkowników. Zatem do dzieła!
Wstęp
Wyobraźmy sobie sytuację, w której użytkownik wchodzi na naszą stronę aby pobrać jakieś dane. Skrypt szybciutko łączy się z bazą i przekazuje mu je najszybciej jak to możliwe. Założenia naszego systemu wymagają jednak, aby każde działanie użytkownika było logowane do bazy. Normalnie połączenie użytkownik->skrypt pozostałoby otwarte, co skutkowałoby tym, że musiałby on bezsensownie czekać na zakończenie działania programu. To jest tylko przykładowa sytuacja, ale są i takie, w których skrypt po wysłaniu odpowiedzi musi się jeszcze wykonywać nawet przez dobre parę sekund. Rozwiązanie, które wam zaprezentuje rozwiąże ten problem.
Sytuacja pierwsza – skrypt zwraca użytkownikowi dane
Skorzystamy tutaj z możliwości PHP do buforowania danych wyjścia. Kluczem do zaoszczędzenia cennego czasu jest tutaj wysyłanie odpowiednich nagłówków, które powiedzą przeglądarce, czy i w którym momencie ma zamknąć połączenie/zakończyć ładowanie strony. Na początek kod:
<?php
// otwieramy bufor
ob_start();
// tutaj przetwarzamy i wyświetlamy wszystkie dane
// pobieramy wielkość bufora
$wielkoscOdpowiedzi = ob_get_length();
// wysyłamy nagłówki mówiące przeglądarce o zamknięciu połączenia
header("Content-Length: $wielkoscOdpowiedzi");
header('Connection: close');
header('Content-Encoding: none');
// wysyłamy dane z bufora
ob_end_flush();
ob_flush();
flush();
// zamykamy obecną sesję
if (session_id()) session_write_close();
//proces który leci sobie dalej w tle
sleep(10);
?>
Teraz małe objaśnienie. Na początku otwieramy bufor, wszystkie dane jakie potem wyślemy w nim lądują, następnym krokiem jest pobranie rozmiaru bufora, aby potem przekazać przeglądarce po ilu bajtach może skończyć ładowanie strony. Potem wysyłamy wszystkie nagłówki, w tym momencie należy pamiętać, że w tym przykładzie kompresja gzip musi być wyłączona. Jest to spowodowane faktem, że normalnie kompresja następuje po wykonaniu skryptu, co skutkuje różnicą pomiędzy wielkością przesyłanych danych a zadeklarowanym rozmiarem w nagłówku Content-Length. Kolejnym krokiem jest wysłanie całej zawartości bufora oraz zamknięcie sesji dla danego pliku tak aby nie nastąpiła interferencja, kiedy użytkownik będzie przeglądał dalej stronę podczas gdy skrypt nie zakończy jeszcze swojego działania. I teraz najlepsze, cała praca skryptu wykonywana po tych działaniach będzie miała miejsce tak jakby “w tle” i nie będzie już odczuwalna dla użytkownika. Warto dodać ignore_user_abort(true); na początku skryptu, co uczyni go niewrażliwym na zatrzymanie poprzez kliknięcie przycisku STOP w przeglądarce.
Sytuacja druga – skrypt nie zwracający żadnych danych
Tutaj sytuacja wygląda prościej i nie wymaga “przemeblowywania” kodu. Jeśli skrypt nic nie wyświetla podczas wykonywania a chcemy uzyskać taki sam efekt jak najszybszego uruchomienia programu i zamknięcia połączenia wystarczy na początku skryptu dodać:
header("HTTP/1.0 204 No Content");
Spowoduje to natychmiastowe zakończenie połączenia przez przeglądarkę/cURL/fsockopen przy dalszym wykonywaniu skryptu. Bajecznie prosta metoda, której warto używać zawsze wtedy, kiedy nie przekazujemy nic do przeglądarki.
Podsumowanie
To będzie na tyle moich dzisiejszych wypocin
Mam nadzieje, że te dwa sposoby pozwolą wam na tworzenie jeszcze bardziej rozbudowanych a mimo to jeszcze szybszych serwisów.
Pozdro!
Super. Jak o drugim sposobie wiedzialem i nawet korzystalem, to pierwszego nigdy jeszcze nie mialem okazji uzyc. A widze ze przyda sie np. przy ajaxach