JustPaste.it

Inne elementy składni - podstawy języka PHP

Inne elementy składni

Include i require

Tworzenie dynamicznych stron byłoby bardzo kłopotliwe, gdybyśmy musieli kopiować wszystkie pracowicie utworzone przez nas funkcje do każdego pliku PHP z osobna. Na szczęście PHP udostępnia mechanizmy na dołączanie jednego skryptu do drugiego. To instrukcje include, require, include_once oraz require_once.

Rozpatrzmy taką sytuację: mamy dwa pliki z funkcjami definiującymi wygląd treści: normalny.php oraz opcjonalny.php. Stworzone w nich są identyczne funkcje różniące się jedynie zawartością. We właściwym skrypcie dołączamy jeden z tych plików decydując o tym, w jakim stylu zaprezentowane zostaną dane na stronie. Oto kod dwóch dołączanych plików.

normalny.php:

<?php

function pokazTytul($tytul)
{
echo '<h1>'.$tytul.'</h1>';
} // end pokazTytul();

function pokazParagraf($tekst)
{
echo '<p>'.$tekst.'</p>';
} // end pokazParagraf();

function pokazListe(Array $tablica)
{
echo '<ul>';
foreach($tablica as $element)
{
echo '<li>'.$element.'</li>';
}
echo '</ul>';
} // end pokazListe();
?>

opcjonalny.php:

<?php

function pokazTytul($tytul)
{
echo '<h1>'.$tytul.'</h1>';
} // end pokazTytul();

function pokazParagraf($tekst)
{
echo '<p style="font-weight:bold;">'.$tekst.'</p>';
} // end pokazParagraf();

function pokazListe(Array $tablica)
{
echo '<ol>';
foreach($tablica as $element)
{
echo '<li>'.$element.'</li>';
}
echo '</ol>';
} // end pokazListe();
?>

Jeszcze raz zwracamy uwagę, że oba pliki tworzą funkcje o takich samych nazwach, dlatego naraz może być załadowany tylko jeden z nich. Oto plik index.php, który na podstawie tego, czy ustawiony jest parametr "styl", decyduje, który z powyższych skryptów zostanie załadowany:

<?php

if(!isset($_GET['styl']))
{
require('./normalny.php');
}
else
{
require('./opcjonalny.php');
}

pokazTytul('Tytuł');
pokazParagraf('To jest paragraf');

pokazListe(array(0 =>
'To',
'Jest',
'Lista'
));
?>

Choć require wywołuje się identycznie, jak funkcję, funkcją nie jest. Różnica między nim, a include jest taka, że pierwsza w przypadku nieznalezienia pliku generuje komunikat Fatal error zatrzymujący skrypt, druga tylko ostrzeżenie. Istnieją także include_once oraz require_once, które są ignorowane, jeśli próbujemy po raz drugi dołączyć ten sam plik.

Budowanie kompletnej strony z mniejszych plików jest bardzo pożyteczne. Generalnie nie zaleca się pisania wszystkiego ciurkiem bez podziału na funkcje, mniejsze moduły itd. gdyż zmniejsza to odporność skryptu na błędy, wprowadza chaos i utrudnia dodawanie/modyfikowanie nowych opcji. Przyjrzyjmy się, jak zatem zorganizować naszą witrynę. Przede wszystkim zróbmy sobie jeden katalog na wszystkie pliki z funkcjami. Może on się nazywać np. includes. Umieszczamy w nim funkcje ułatwiające komunikację z bazą danych, przetwarzanie tekstu, autoryzację i wykonujące wszystkie inne rutynowe operacje. W katalogu głównym zawierającym skrypty dostępne z przeglądarki (np. index.php, rejestracja.php itd.), umieszczamy także jeden plik o nazwie common.php. Ma on następującą postać:

<?php

require('../includes/database.php');
require('../includes/text.php');
require('../includes/session.php');
require('../includes/authorize.php');
require('../includes/templates.php');
require('../includes/functions.php');
require('../includes/layout.php');
require('../includes/main.php');

initSystem();

?>

common.php ma jedno tylko zadanie: załadować cały silnik i zainicjować jego pracę. Teraz w plikach typu index.php generujących właściwą zawartość strony wpisujemy na początku:

<?php
require('./common.php');

// tutaj kod strony

?>

W taki właśnie sposób wprowadziliśmy organizację w strukturę naszej witryny. Jeżeli silnikowi przybędzie dodatkowy plik, dopisujemy go jedynie do common.php, a będzie on dostępny na wszystkich podstronach.

Przyszła pora na przyjrzenie się wydajności procesu dołączania. Wiele innych źródeł zachęca do dołączania plików bez podawania ścieżki, np.

require('plik.php');

Wbrew pozorom, niepodanie przed nazwą zwyczajnego ./ ma wpływ na wydajność.

require('./plik.php');

Różnice leżą w przetwarzaniu każdej z tych ścieżek przez PHP. W konfiguracji interpretera jest dyrektywa include_path. Informuje ona, jakie ścieżki mają być przeszukiwane po kolei w przypadku niezdefiniowania położenia pliku. Tak więc przy pierwszym sposobie PHP testuje każdy z wprowadzonych tam katalogów, a sprawdzanie to nieco trwa. Owszem, istnieją sytuacje, kiedy trzeba się tym posłużyć (np. przy dołączaniu bibliotek PEAR instalujących się w systemie w dziwnych miejscach), ale na co dzień pożałowanie tych dwóch dodatkowych znaków może nas kosztować nawet dwukrotny spadek wydajności już dla zaledwie kilku dołączanych w ten sposób skryptów. ./ wyraźnie informuje interpreter: "interesuje mnie tylko bieżący katalog. Nie szukaj nigdzie indziej."

Kolejną istotną kwestią jest bezpieczeństwo. W sieci wciąż można spotkać wiele artykułów, które polecają taki sposób wykonania modułowości:

<?php

include($_GET['co']); // NIGDY TEGO NIE UŻYWAJ!

?>

Jest to jawne pogwałcenie zasad bezpieczeństwa - nigdy nie zezwalać użytkownikowi na dostęp do jakiegokolwiek pliku. Oczywiście, dla większości osób adres www.strona.pl/?co=informacje.html nie będzie podejrzany, ale wystarczy tu już nieco lepsza znajomość PHP i systemów operacyjnych, by wpisać www.strona.pl/?co=/etc/passwd i w ten sposób odczytać listę haseł serwera. Wydawać się to może śmieszne i prostackie, ale przez zwyczajne niedbalstwo programistów "padło" wiele witryn, w tym także tych należących do publicznych instytucji. Proponowanie powyższego sposobu jako recepty na modułowość to najprostsze w świecie zaproszenie hackera do zniszczenia naszej witryny. Jeżeli naprawdę potrzebujemy czegoś takiego, __musimy__ sprawdzić, że podana nazwa zawiera wyłącznie dozwolone znaki. Nie pomoże dodawanie nazwy katalogu przed ścieżką (atakujący może to ominąć podając ".."), ani doklejanie rozszerzenia do nazwy plików (przez bug w PHP można je odciąć za pomocą znaku NUL (%00 w URL)).

 

Stałe

Spójrzmy raz jeszcze na przykład z plikiem common.php. Jak nietrudno się domyślić, większe projekty składają się z pewnej liczby katalogów. Pojawia się tu problem, skąd skrypt ma wiedzieć, gdzie leżą potrzebne mu pliki? Teoretycznie możemy ścieżki wpisywać ręcznie przy każdej konieczności, lecz jest to bardzo nieefektywne. Jeżeli dodatkowo aplikacja posiada jakiś silnik ze zbiorem używanych w różnych miejscach funkcji, może nawet okazać się to niemożliwe. Typowa strona internetowa składa się bowiem z paru sekcji, np. panelu administracyjnego, części głównej, forum, zlokalizowanych najczęściej w osobnych podkatalogach. Gdybyśmy na stałe zdefiniowali jakąś ścieżkę w silniku, nie wiedzielibyśmy, do której sekcji ona prowadzi.

Zdefiniujmy zatem wszystkie używane w projekcie ścieżki w pliku common.php za pomocą zmiennych. Już na pierwszy rzut oka widać, iż rozwiązanie to nie jest najlepsze, bowiem każdą zmienną ze ścieżką musimy przenosić do funkcji z użyciem global, a ponadto istnieje ryzyko, że w którymś miejscu omyłkowo ją nadpiszemy. Remedium na te kłopoty są stałe. Są to aliasy na pewne wartości, których po utworzeniu nie można już modyfikować. Najogólniej wykorzystuje się w je dla często powtarzających się w skrypcie wartości. Przyjrzyjmy się poniższemu skryptowi:

<?php
define('DIR_GLOWNY', './');
define('DIR_SILNIK', './includes/');
define('DIR_ZDJECIA', './zdjecia/');

require(DIR_SILNIK.'autoryzacja.php');
require(DIR_SILNIK.'funkcje.php');

?>

Do zdefiniowania stałych używamy funkcji define(), w której definiujemy nazwę stałej oraz jej wartość. Zwyczajowo stałe mają nazwy złożone z samych dużych liter, a wartościami mogą być wyłącznie typy skalarne (czyli nie tablice, nie obiekty oraz nie zasoby). Podczas wywoływania stałych nie poprzedzamy znakiem dolara.

Oto wszystkie cechy stałych:

  1. Stałe nie mają znaku dolara ($) przed nazwą
  2. Stałe mogą być definiowane oraz używane wszędzie bez zważania na zasady dotyczące zakresu ich dostępności
  3. Stałe nie mogą być ponownie definiowane lub "oddefiniowane" po tym jak raz zostały zdefiniowane
  4. Stałe mogą zawierać tylko wartości skalarne

Oprócz przechowywania ścieżek do katalogów, stałe mają zastosowanie podczas pisania bibliotek programistycznych. Często zdarza się, że do funkcji musimy przekazać jakąś wartość liczbową identyfikującą konkretny stan. Ponieważ spamiętywanie cyferek jest uciążliwe, tworzy się dla nich stałe o bardziej czytelnych nazwach. Możemy to pokazać na podstawie funkcji error_reporting() pozwalającej ustawić poziom raportowania błędów przez PHP. Da się ją wywoływać w ten sposób:

<?php
error_reporting(1 | 2 | 4 | 8);
?>

Lecz dla osoby postronnej cyfry połączone operatorem alternatywy bitowej są zwyczajnie niezrozumiałe. Zamiast cyfr, można użyć odpowiadające im stałe zdefiniowane przez PHP:

<?php
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
?>

Kolejnym ciekawym zagadnieniem, którego realizację ułatwiają stałe, jest przekazywanie parametrów do funkcji w sposób pokazany powyżej. Rozpatrzmy przypadek systemu raportowania stanu pojazdu. Stworzyliśmy sobie funkcję raportującą pobierającą pięć wartości logicznych (prawda-fałsz) opisujących, które elementy są sprawne, a które nie. Nasz skrypt wygląda tak:

<?php
// Raportowanie stanu pojazdu
function stan($silnikOK, $kolaOK, $swiatlaOK, $skrzyniaOK, $paliwoOK)
{
if($silnikOK)
{
echo 'Silnik jest sprawny<br/>';
}
if($kolaOK)
{
echo 'Koła są sprawne<br/>';
}
if($swiatlaOK)
{
echo 'Światła są sprawne<br/>';
}
if($skrzyniaOK)
{
echo 'Skrzynia jest sprawna<br/>';
}
if($paliwoOK)
{
echo 'Paliwo jest w baku<br/>';
}
} // end stan();


stan(true, false, false, true, true);
?>

Znów mamy identyczny problem: wywołując funkcję gdzieś w skrypcie musimy pamiętać, jaka jest kolejność parametrów, a postronna osoba już w ogóle nie zrozumie, co do czego ma iść. Wykorzystajmy więc fakt, iż komputer zapisuje wszystko w postaci zerojedynkowej i prześlijmy wszystkie parametry jako jedną liczbę o długości pięciu bitów (od 0 do 32). W stałych zdefiniujemy nazwy poszczególnych elementów przypisując im kolejne potęgi dwójki. Następnie za pomocą operatora alternatywy bitowej zbudujemy z nich liczbę:

<?php
define('SILNIK', 1);
define('KOLA', 2);
define('SWIATLA', 4);
define('SKRZYNIA', 8);
define('PALIWO', 16);

// Raportowanie stanu pojazdu
function stan($stan)
{
if($stan & SILNIK)
{
echo 'Silnik jest sprawny<br/>';
}
if($stan & KOLA)
{
echo 'Koła są sprawne<br/>';
}
if($stan & SWIATLA)
{
echo 'Światła są sprawne<br/>';
}
if($stan & SKRZYNIA)
{
echo 'Skrzynia jest sprawna<br/>';
}
if($stan & PALIWO)
{
echo 'Paliwo jest w baku<br/>';
}
} // end stan();


stan(SILNIK | SKRZYNIA | PALIWO);
?>

Operatorem koniunkcji bitowej możemy sprawdzić, czy dany element jest sprawny. Cały ten sposób jest określany mianem ustawiania flag (każda stała to jedna flaga) i jest powszechnie wykorzystywany w programowaniu ze względu na wydajność. Efektywniej jest przesłać pięć wartości jednym parametrem, niż tyle samo pięcioma. Przyjrzyjmy się zatem tajemnicy tego sposobu. Najpierw - jak reprezentowana jest każda flaga w postaci binarnej:

SILNIK (1):   00001
KOLA (2): 00010
SWIATLA (4): 00100
SKRZYNIA (8): 01000
PALIWO (16): 10000

W każdej liczbie będącej potęgą dwójki "zapalony" jest tylko jeden bit - z tej własności korzystamy. Kiedy składamy kilka potęg dwójki operatorem alternatywy bitowej, zapalamy tym samym poszczególne bity:

SILNIK | SKRZYNIA | PALIWO:

SILNIK (1): 00001
SKRZYNIA (8): 01000
PALIWO (16): 10000
-------------------
REZULTAT: 11001

Jeżeli w danej kolumnie którakolwiek z wartości "zapala" bit, będzie on zapalony także w rezultacie. W operatorze koniunkcji bitowej używanym do testowania, dany bit rezultatu jest zapalany jedynie w wypadku jego obecności naraz w obu podanych liczbach:

$rezultat & SWIATLA
REZULTAT: 11001
SILNIK (1): 00001
-------------------
00001

Otrzymujemy liczbę większą od zera, czyli prawdę logiczną. Pytając się o flagę SWIATLA, zostanie wykonane takie działanie:

$rezultat & SILNIK
REZULTAT: 11001
SWIATLA (4): 00100
-------------------
00000

Teraz żadna z jedynek nie powtarza się w obu wierszach naraz, więc działanie da nam wynik 0, czyli fałsz. To jest cała tajemnica tego sposobu.

Referencje

Referencja to swego rodzaju alias na zmienną, dzięki czemu może ona występować pod dwoma nazwami. Popatrzmy na taki przykład:

<?php

$a = 5;
$b = &$a; // tworzymy referencje

echo 'B: '.$b.'; A: '.$a.'<br/>';
$b = 6;
echo 'B: '.$b.'; A: '.$a.'<br/>';
$a = 7;
echo 'B: '.$b.'; A: '.$a.'<br/>';
?>

Referencję można utworzyć, stawiając przed zmienną źródłową znak &. Po uruchomieniu przykładu zobaczymy, że modyfikując jedną z tych zmiennych, zmieniała się także druga. Jest tak dlatego, że obie te zmienne reprezentują w rzeczywistości tę samą wartość.

Referencje są bardzo użyteczne przy pracy z dużymi zbiorami danych. Przeprowadzając operację:

$b = &$a;

nie wykonujemy żadnego żmudnego kopiowania tych danych, lecz tworzymy do nich kolejny odnośnik. Jest to wielokrotnie szybsze, lecz sprawia, że modyfikacja "pseudo kopii" odbije się także na oryginale.

Referencje w połączeniu z innymi strukturami nadają im pewne dodatkowe właściwości. Przypomnij sobie pętlę foreach. Wspominaliśmy tam, iż dodatkowe zmienne reprezentujące indeks oraz wartość elementu tablicy są kopiami, dlatego nie można ich jawnie stosować do modyfikowania przetwarzanej tablicy. W PHP 5 ten problem zniknął, ponieważ możemy poinformować PHP, że zmienna wartości ma być referencją. Dzięki temu możliwe będzie modyfikowanie przez nią zawartości tablicy:

<?php

$tablica = array(0 => 1, 2, 3, 4, 5, 6);

foreach($tablica as $id => &$element)
{
$element = rand(1, 6);
}

print_r($tablica);

?>

W tym przykładzie wypełniamy tablicę losowymi numerami poprzez zmienną $element tworzoną przez pętlę. Jest to możliwe, gdyż zadeklarowaliśmy znakiem &, iż ma to być referencja do właściwego elementu. Gdyby usunąć ten znak, funkcja print_r() pokazałaby nam dalej pierwotną zawartość tablicy. Tak się jednak nie dzieje, zatem sposób okazuje się skuteczny.

Dzięki referencjom funkcje mogą pozornie zwracać więcej wartości - poprzez referencyjne parametry. Napiszemy sobie teraz funkcję do kolorowania tekstów dłuższych, niż 5 znaków. W zależności od długości funkcja zwraca wartość 1 lub 0. Zmodyfikowany tekst jest oddawany tą samą drogą, którą się dostał, tj. referencyjnym parametrem. W celu przetestowania przetworzymy sobie dwa ciągi. Jeden liczy sobie trzy znaki, drugi jest wyraźnie dłuższy.

<?php

function przetworz(&$tekst)
{
if(strlen($tekst) > 5)
{
$tekst = '<font color="red">'.$tekst.'</font>';
return 1;
}
return 0;
} // end przetworz();

// Probujemy przerobic krotki tekst
$tekst = 'Jan';

if(przetworz($tekst))
{
echo 'Przetworzony tekst: '.$tekst.'<br/>';
}
else
{
echo 'Błąd: za krótki tekst! '.$tekst.'<br/>';
}

// A teraz dlugi
$tekst = 'Ala ma kota';

if(przetworz($tekst))
{
echo 'Przetworzony tekst: '.$tekst.'<br/>';
}
else
{
echo 'Błąd: za krótki tekst! '.$tekst.'<br/>';
}

?>

Sposób ten także działa, ponieważ w drugim (dobrym) przypadku po wywołaniu funkcji przetworz($tekst) w zmiennej znalazła się zmodyfikowana postać.

Przekazywanie parametrów poprzez referencje ma pewne ograniczenia. Nie można do takiego parametru podać wartości stałej, ponieważ referencja musi operować na zmiennych.

<?php
function funkcja(&$a)
{

}

// proba 1
funkcja('test');

// proba 2
$zmienna = 'test';
funkcja($zmienna);

?>

Pierwszy zapis spowoduje błąd, gdyż nie przekazaliśmy wartości w postaci zmiennej. Wystrzegaj się także wywoływania funkcji ze zmuszaniem do przekazywania referencji, gdy funkcja sobie wyraźnie tego nie życzy: funkcja(&$zmienna). Zapis taki był poprawny jeszcze w PHP 4, lecz w PHP 5 został wycofany i domyślnie pokazuje się ostrzeżenie przy napotkaniu gdziekolwiek takiego fragmentu.

Eval

Niektóre algorytmy pisane przez programistów PHP generują kod w tym języku, który następnie jest wykonywany. Można go zapisać do pliku i załadować instrukcją include, lecz nie zawsze jest to optymalne rozwiązanie. PHP oferuje swoim programistom instrukcję eval, która wykonuje podany w ciągu tekstowym kod jako skrypt PHP.

<?php
$zmienna = 5;

eval('echo $zmienna;');
?>

Eval najpierw spowoduje kompilację ciągu echo $zmienna;, a następnie jego wykonanie, czego efektem będzie wyświetlenie się w przeglądarce wartości zmiennej. Nie nadużywaj tej własności zbyt często. Wykonywanie tworzonego przez nas kodu PHP jest ciekawą opcją, lecz mogącą prowadzić do spadku wydajności, trudnych do zauważenia błędów i luk bezpieczeństwa. PHP jest tak zaprojektowany, że nigdy nie potrzeba w nim używać eval(). Oto dwa przykłady, kiedy nie trzeba stosować eval.

1. Chcemy wykonać funkcję, której nazwę mamy zapisaną w zmiennej $funkcja. Choć możemy napisać

eval($funkcja.'($a, $b, $c)');

do dyspozycji mamy o wiele lepszy sposób:

$funkcja($a, $b, $c);

2. Chcemy wstawić w wyrażeniu wartość zmiennej, której nazwa zapisana jest w innej zmiennej (np. $zmienna). Zamiast pisać

eval('echo $'.$zmienna.';');

możemy zrobić:

echo $$zmienna;

Oba powyższe przykłady wykonają się szybciej, ponieważ są kompilowane wraz z właściwym skryptem. W przypadku eval PHP musi natomiast po raz drugi uruchamiać kompilator.

 

Treść pochodzi ze strony WikiBooks i jest udostępniana na licencji GNU FDL

 

Uwaga! Uwaga!
Nigdy nie przekazuj do instrukcji eval danych z tablic $_POST, $_GET albo $_COOKIE, ponieważ realnie zagrażasz w ten sposób zarówno swojej aplikacji, jak i serwerowi.

 

Autor: WikiBooks