Optymalizacja PHP: file_exists

08 maja 2009

Funkcja file_exists jest wolna. To fakt niezaprzeczalny, generalnie każde odwołanie PHP do plików/katalogów jest wolne i nie da się tego przeskoczyć.

Z drugiej strony we wszystkich kursach i książkach na temat PHP jesteśmy karmieni ładnymi bloczkami kodu gdzie nieodłącznym elementem jest sprawdzanie czy dany plik istnieje przed pobraniem jego zawartości czy dołączeniem do skryptu.

Co w takim razie robić? Używać czy nie używać file_exists? Odpowiedź brzmi: to zależy.

Jeżeli jakiś plik do którego za chwilę zamierzasz się odwołać pochodzi z niepewnego źródła lub np. pracujesz nad projektem w którym grzebie mnóstwo ludzi lub jeżeli istnieje jakikolwiek inny powód przez który nie masz 100% pewności, że ten plik tam jest to używaj file_exists.

Ale jeżeli sam wgrałeś/wygenerowałeś wcześniej ten plik i wiesz, że on tam jest to, na miłość Boską, nie używaj file_exists tylko dlatego, że tak ładniej i profesjonalniej wygląda! Nie popadaj w skrajności bo wkrótce zaczniesz sprawdzać przed każdym include/require czy interesujący Cię plik jest na dysku.

Pamiętaj, programuj z głową. Czasami trzeba pisać systemy idiotoodporne ale czasami wystarczy napisać kawałek kodu, który wykonuje to i tylko to co od niego oczekujesz, bez zbędnej uniwersalności i wodotrysków.

Dodaj komentarz

14 odpowiedzi dla tego wpisu

  1. SpeX napisał:

    A jak sprawdzić czy plik graficzny hostowany na zewnętrznych hostach typu imageshack.us/ istnieje i nie jest przypadkiem zwracany jakiś obrazek zastępczy o zbanowaniu strony lub coś podobnego?

  2. MariuszT napisał:

    Od wersji PHP 5.0 możesz w funkcji file_exists skorzystać z jakiegoś protokołu a nie sprawdzać tylko lokalne pliki. W skrócie, możesz podać adres HTTP. Pamiętaj jednak, że działanie tej funkcjonalności jest uzależnione od konfiguracji serwera. Jeżeli z jakiegoś powodu to Ci nie zadziała to przejrzyj komentarze na stronie http://pl.php.net/file_exists. Tam jest sporo różnych rozwiązań.

    Natomiast uzyskanie pewności, że zwrócono prawidłowy obrazek a nie zastępczy to już całkowicie inna bajka… Pół biedy gdy jest przekierowanie. Wtedy sprawdzasz adres na jaki jesteś przekierowywany i jeżeli zgadza się on z tym, który ma obrazek z informacją o banie to wystarczy prosty warunek w PHP.

    Ale jeżeli obrazki są zastępowane to już jest poważny problem. Jeżeli musiałbym to oprogramować to sprawdzałbym sumę kontrolną ściągniętego pliku i porównywał ją z wcześniej zapisaną sumą obrazka zbanowanego. W ostateczności można sprawdzać grupę wybranych pikseli i porównywać czy kolorystycznie zgadzają się z obrazkiem bana. Ale to bardzo mało wydajna metoda i może być zawodna.

    Nie chcę Cię martwić ale w przypadku takiego problemu nie ma żadnej stuprocentowej metody. Bo nawet jeżeli masz pewność, że coś działa teraz to za miesiąc oni zmienią coś na swoim serwerze i u Ciebie wszystko się może wywalić.

  3. KMO napisał:

    Ciekawa porada, jeśli można to proszę o więcej z zakresu operacji na plikach i katalogach, gdyż pracuję nad własnym unikatowym systemem newsów z dodatkami.

  4. SpeX napisał:

    Chyba faktycznie najlepiej będzie ręcznie sprawdzać czy plik istnieje, ale chyba lepszym rozwiązaniem niż sprawdzanie sumy kontrolnej będzie sprawdzenie wymiarów/wagi pliku. Nie jestem obeznany w tym temacie, ale to chyba dało by się wprowadzić po stronie serwera zdjęć, czyli by nie pobierać grafikę by wygenerować jej sumę.

    A po prostu szukam jakiegoś sposobu na oszczędzenie trochę transfery na galerii zdjęć.

  5. MariuszT napisał:

    Hmmm… Z wymiarami pliku może być ciężko ale pobrać info tylko o objętości raczej powinno wypalić. Musisz kombinować z wysłaniem żądania typu HEAD. Pobierzesz w ten sposób tylko nagłówki a z nagłówków odczytasz objętość pliku. W CURL jest do tego specjalne ustawienie CURLOPT_NOBODY ale i zwykłym fopen to obsłużysz.

    Mam nadzieję, że Cię nie kłamię bo sam tego nie testowałem. Może w wolnej chwili na to zerknę, może nawet zaraz :)

  6. MariuszT napisał:

    Przetestowałem coś i działa ;) Jeszcze dzisiaj dostaniesz gotową funkcję do pobrania tylko nagłówków i może jeszcze dopiszę dla Ciebie coś co wyciągnie Ci tylko info o objętości żądanej treści.

  7. SpeX napisał:

    Dzięki, teraz tylko zmodyfikować MySQLa by każde zdjęcia miało 3 lokalizację (mój serwer i 2 zewnętrzne) oraz jakieś parametry do sprawdzania.

    Jakie zewnętrzne serwery do hostowania zdjęć polecacie?

  8. MariuszT napisał:

    Obiecałem i cisza… Sorki ale jestem strasznie zawalony robotą, jak tylko znajdę chwilę to napiszę Ci kod ale musisz jeszcze uzbroić się w cierpliwość.

  9. Mateusz Żeromski napisał:

    Hmm moim zdaniem piszesz małą głupotę

    File extist uzywa sie jak chce się sprawdzić czy jest plik – widocznie użycie jest konieczne, wiec jak możesz namawiać do nie używania tego, a nie sorki – jak trzeba sprawdzić czy plik istnieje można użyć…..

    Podczas wklejania plików php, niezbędnych do działania używa się require zamiast include i problem z głowy.

    W przypadku require() – jeżeli nie ma pliku – skrypt kończy działanie, include() – idzie dalej.

    Moim zdaniem trochę przesadzasz….

  10. MariuszT napisał:

    Napisałem wyraźnie aby nie nadużywać tej funkcji jeżeli trzeba. Tylko i aż tyle. Kompletnie nie rozumiesz moich intencji.

  11. kmg napisał:

    MariuszT: file_exist – pewnie wolne, nie wiem, nie testowałem. Nic nie stoi jednak na przeszkodzie, aby dopisać np. w asm coś, co sprawdzać będzie szybciej. Tylko po co?

    Cieżko mi odnieść się do PHP, gdyż raczej już w tym się nie specjalizuje. Niemniej od wersji 5.0 (nie wiem, może 5.1) istnieje obsługa wyjątków – idealnie powinna zastąpić file_exist, rzucając wyjątek gdy plik nie istnieje, brakuje phpowi odpowiednich uprawnień, etc. Dlatego powiedział bym rzeczywiscie: programuj z głową, korzystaj z innowacji wprowadzonych w celu ułatwienia życia programiście.

    Dlaczego nie zrezygnowano z file_exists? na pewno na rzecz kompatybilności wstecz. Ale nie tylko – trzeba pamietać, że czasami w zależności od tego, czy zasób istnieje (bo nie można ograniczać się wyłącznie do plików) możemy (bądź nie) robić różne rzeczy.

    Prosty przykład:
    Jest drukarka? Wyświetlamy przycisk do drukowania (oczywiscie to troche bardziej złożone, ale chodzi o ogolny schemat).

    Oczywiscie nie należy równiez przesadzac w drugą stronę – wyjątki to bardzo rozbudowany mechanizm, nie wolno spłaszczać ich zastosowań tylko do sprawdzenia, czy wystąpił jakiś błąd.

    KMO: wybacz, ale „unikatowym systemem newsów z dodatkami” brzmi śmiesznie, pomijając już to, że zalatuje (wiem, oklepane stwierdzenie) „wyważaniem otwartych drzwi”. Chcesz zrobić coś unikatowego? Zrób więc cos, czego nie było. Opracuj nowy algorytm, nowy jezyk lub nawet połącz istniejące technologie aby uzyskać coś rew(e)/(o)l(a)/(u)cyjnego . Bo co unikatowego bedzie miał Twój system? Przycisk „dodaj” pomiędzy polami autor/temat? (wiem, że brzmi jak atak i wypowiedź jest troche ostra, ale ludzie – nie każdy wasz skrypt/program jest lub będzie unikatowy. Niezależnie od tego jakie kryteria unikatowości wybierzecie)

    SPEX: oszczedzanie transferu kosztem kogoś innego: po pierwsze nielegalne (choć teoretycznie polski wymiar sprawiedliwości nie opiera się na precedensach), po drugie – obecnie transfer jest tani na barszcz. Równie dobrze możesz przecież pisać galerie na plikach zamiast bazy danych – ponieważ takie konta są tańsze.

    MATEUSZ ŻEROMSKI: to ty piszesz małą głupotę. Wystarszy wziąć dowolną książkę o PHP – czy to polską czy zagraniczną. Ludzie nie rozumieją, po co jest file_exists, i kiedy jest potrzeba sprawdzenia tego, czy plik/zasób istnieje. A co gdy pisze skrypt, który w zależności od tego czy został wrzucony plik: config-detail.php dołącza albo go, albo (gdy nie istnieje) config-default.php ? Abstrachując od zawartości, samo require (choć z racji typu plików – konfiguracyjne) będzie oczywiście stosowane, ale samo nie wystarczy. Tu właśnie przyda się file_exists (bo wyjątków tutaj sensu stosować nie ma).
    Funkcje mają współdziałać, a jeśli ktoś myli mechanizmy:

    if(file_exists(file))
    include(file);

    od:
    require(file);

    To chyba nie ma wielkiego pojęcia o programowaniu.
    bo pierwszy ma dołączyć plik tylko jeśli istnieje, a drugi – wymagać dołączenia tego pliku. Przypadek pierwszy: jeśli pliku nie ma, wystarczy, że dołącze inny, nie potrzebuje warningów. Czy to samo osiągnę stosując require()? Nie. Można oczywiscie wyłączyc warningi (vide: error_reporting()), ale funkcja
    nie służy do tego (pomijajac juz to, ze śmieci wtedy po logach)


    Ogolnie chetnie podyskutuje, choc ostatnio z powodu mojej nie-unikatowej pracy (przede wszystkim naukowej) czasu zbyt wiele nie mam. Zwłaszcza z Tobą Mariusz – jakoś od wypadku czasu zbytnio nie było.

  12. Mateusz Żeromski napisał:

    @KMG – nie rozumiem Ciebie, zobacz o czym ja napisałem, w skrócie: file_exist uzywa się po to aby sprawdzić czy plik istnieje – i tylko po to, i jak się tego używa to ważne jest aby się dowiedzieć czy plik istnieje. Jeżeli zaś chcemy wkleić plik niezbędny do działania – używa się require.

    Odnośnie Twojego przykładu można również użyć @include(‘file’); jak jest to wklei jak nie to nie i nie pokaże błędu.

    W przypadku aplikacji gdzie moduły są instalowane poprzez kopiowanie całej struktury katalogów i plików, file_exist jest niezbędne, np aby połączyć istniejące pliki konfiguracyjne, tłumaczeniowe itp.

    Wczoraj napisałem na swoim blogu: w permanentnie zmieniającym się środowisku dwa lata to kupę czasu. Wiekszość książek o programowaniu niestety ale powstało już dawno dawno temu, dlatego ja nie polegam na literaturze podczas nauki technologii, i Tobie też nie polecam. Jest internet, fora blogi itp. Książki można czytać o ekonomii, filozofii itp. Informatyka za szybko się zmienia. :)

  13. kmg napisał:

    @Mateusz: Jeśli nie rozumiesz, przeczytaj ponownie. Pokaż mi na przykładzie jak używając @include w zależności od istnienia pliku wstawisz różne pliki (czyt. jesli plik a.php istnieje to go wstaw, jeśli nie – wstaw plik b.php). Oczywiscie możesz wykorzystać również instrukcje warunkowe (choć w przypadku wyjątków nie są one potrzebne).
    Teraz rozumiesz przykład? Pomijając, że @include będzie sypać błędami po logach (co przy dużej liczbie odwiedzających będzie conajmniej uciążliwe – wiec to nie to samo co if(file_exists(file)) include(file); ).

    Odniosę sie również do książek: są książki aktualne i książki nieaktualne. W języku polskim raczej te drugie (wynika to z procesu translacji). Mam przyjemność być autorem/współautorem paru publikacji – przede wszystkim związanych z telekomunikacją (która zmienia się bardziej dynamicznie niż informatyka). Niektóre z nich oczywiście posiadają dosyć sporo stron (liczonych w setkach). A fakt, że nie polegasz na literaturze podczas nauki technologii mówi wyłącznie o tym, że albo nie lubisz czytać (co jest bzdurą, skoro czytasz „Internet, fora, blogi itp”, albo niewłaściwie dobierasz materiały do nauki.

    Pamiętaj, że w klasyfikacji materiałów to rozprawy i inne publikacje naukowe zajmują czołowe miejsca. Książki jakie drukuje np. Helion są tylko troszkę wyżej od Internetu (ale zawsze wyżej!), który zajmuje w takiej klasyfikacji ostatnie miejsce. Informatyka szybko się zmienia, co nie oznacza ze jako autorzy nie aktualizujemy swoich publikacji. Dlatego wszedzie (!!!) znajdziesz datę wydania. Również w przypadku translacji tytułu na język polski w książce (lub innej publikacji) znajduje się informacja o dacie publikacji oryginału.
    Polecam lekturę pozycji (tytuł z pamięci, może się troszke różnić) „Jak napisać rozprawę doktorską”. Znajduje się tam między innymi klasyfikacja materiałów źródłowych.

    Oraz życzę szczęścia w poszukiwaniach aktualnych książek/publikachach.

    P.s. Oczywiscie sam również korzystam z książek, nawet częściej niż z Internetu. Bo potrafią być one bardziej aktualne, niż (nazwijmy to ogólnie) źródła internetowe. Jednak w mojej bibliotece 90% materiałów jest w języku angielskim.

    P.s.2. „W przypadku aplikacji gdzie moduły są instalowane poprzez kopiowanie całej struktury katalogów i plików, file_exist jest niezbędne, np aby połączyć istniejące pliki konfiguracyjne, tłumaczeniowe itp.” pomijajac o tym ze 90% tego zdania to teza z mojego poprzedniego komentarza, to pozostałe 10% jest błędne. Dlaczego? Bo stosując wyjątki nie potrzebuje file_exist aby to wszystko zrobić.

  14. mateusz napisał:

    @kmg – odnośnie include piszemy o tym samym, a dopiero teraz zauważyłem config-detail.php/config-default.php :) dlatego mój kom mógł się okazać nieaktualny.

    Taka mała ciekawostka odnośnie tematu:

    Zdognie z nanjwoymszi baniadmai perzporawdzomyni
    na bytyrijskch uweniretasytch nie ma zenacznia kojnolesc
    ltier przy zpiasie dengao solwa. Nwajzanszyeim jest, aby prieszwa
    i otatsnia lteria byla na siwom mijsecu, ptzosałoe mgoą być w niaedziłe
    i w dszalym cąigu nie pwinono to sawrztać polbemórw ze zozumierniem tksetu.
    Dzijee sie tak datgelo, ze nie czamyty wyszistkch lteir w sołwie,
    ale cłae sołwa od razu.

    Książki – masz rację. Jednak nigdy bym nie zgodził się z tezą że książki nadążą za technologią – owszem naukę najlepiej rozpocząć od książki, bo przepisując przykłady z ksiązki uczymy się więcej niż metodą kopiegopasta :) .
    Doprecyzuję jeszcze moją myśl.
    Piszę tutaj o książkach z zakresu programowania, bo taki był tytuł wpisu. Jeżeli chodzi o inne dziedziny nawet informatyki to jestem zwolennikiem aby one były pierwszym źródłem informacji, np projektowanie aplikacji/interfejsów, planowanie czasu/pracy, zarządzanie itp itd.

    Rok temu miałem przygodę z nauką pythona, która mi się powiodła :) .Pierwszym krokiem była książka w firmie, była nudna i mało konkretna, chyba to były rozmówki w python od helion. Jak zobaczyłem co się dzieje w tej książce, wklepałem do googla „python tutorial, python how to start” i dopiero wtedy moja nauka dostała kopa :) Dlatego w przypadku efektywnej nauki programowania jestem zwolennikiem internetu a nie książek.

Odpowiedz



Podobne wpisy: