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!

  1. 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 ;)

  1. No trackbacks yet.

Dodaj komentarz

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Zmień )

Twitter picture

You are commenting using your Twitter account. Log Out / Zmień )

Facebook photo

You are commenting using your Facebook account. Log Out / Zmień )

Connecting to %s

Follow

Otrzymuj każdy nowy wpis na swoją skrzynkę e-mail.