PHP/Inne elementy składni

< PHP
Poprzedni rozdział: Funkcje
Następny rozdział: Każdy popełnia błędy

Inne elementy składni

edytuj

Include i require

edytuj

Tworzenie dynamicznych stron byłoby bardzo kłopotliwe, gdybyśmy musieli pracowicie kopiować wszystkie 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. Służą do tego 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, dlatego zapis np. $zmienna = require('skrypt.php'); jest nieprawidłowy. Różnica między nim, a include polega na sposobie obsługi błędów. Instrukcja require 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. Pozwala to uniknąć omyłkowego, kilkukrotnego ładowania tych samych funkcji, co oczywiście prowadziłoby do błędu. Powinniśmy używać ich tylko wtedy, kiedy liczymy się z taką możliwością.

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. Dodatkowo tworzymy katalog actions, w którym będzie zawarty kod różnych podstron takich, jak rejestracja czy lista artykułów. Oba katalogi powinny być umieszczone poza katalogiem publicznym dostępnym z przeglądarki. Tam umieścimy tylko jeden plik, index.php, który będzie zarządzać uruchamianiem poszczególnych akcji. Oto i on:

<?php
require('../includes/config.php');
require('../includes/dispatch.php');
require('../includes/session.php');
require('../includes/authorize.php');
require('../includes/templates.php');
require('../includes/functions.php');
require('../includes/layout.php');

initSystem();
dispatchAction(isset($_GET['act']) ? $_GET['act'] : 'index');

Funkcję dispatchAction() umieszczamy w jednym z plików w katalogu /includes jako funkcję silnika naszej strony (np. w dispatch.php). Jej zadaniem jest odpalenie wybranej akcji, a może ona wyglądać następująco:

<?php
function dispatchAction($action)
{
   if(!ctype_alpha($action))
   {
      displayError('Nazwa akcji zawiera nieprawidłowe znaki.');
   }
   if(!file_exists('../actions/'.$action.'.php'))
   {
      displayError('Podana akcja nie istnieje.');
   }
   require_once('../actions/'.$action.'.php');
   $action .= 'Action';

   $action();
} // end dispatchAction();

Nazwa akcji jest tłumaczona na ścieżkę do pliku w katalogu /actions. Wklejając jakiekolwiek dane zewnętrzne do ścieżek używanych przez system musimy zwracać szczególną uwagę na bezpieczeństwo. Gdyby jakiś wścibski użytkownik wywołał naszą stronę jako index.php?act=../includes/dispatch, albo jeszcze jakiś inny plik, moglibyśmy mieć poważne problemy. Dlatego najpierw sprawdzamy funkcją ctype_alpha() czy podana nazwa akcji składa się wyłącznie z liter. Teraz mamy pewność, że działalność użytkownika będzie ograniczona wyłącznie do plików PHP w katalogu /actions. Oczywiście musimy jeszcze funkcją file_exists() upewnić się, że odpowiedni plik istnieje i dopiero wtedy możemy go bezpiecznie załadować.

Zwróćmy uwagę, co dzieje się później. Zakładamy, że plik z akcją będzie zawierać funkcję o nazwie nazwaakcjiAction(). Dlatego do nazwy akcji doklejamy słowo Action i wywołujemy funkcję, wczytując jej nazwę ze zmiennej (podświetlona linia 14). Jest to jedna z ciekawych właściwości PHP i warto o niej pamiętać, aczkolwiek w przypadku przyjmowania danych z zewnątrz także zwracamy uwagę na bezpieczeństwo. W naszym przypadku załatwiają je już mechanizmy przeznaczone dla require_once.

W ramach ćwiczenia spróbuj uzupełnić czymś pozostałe pliki i napisać akcję index, która wyświetli jakiś tekst powitalny.

Stałe

edytuj

Spójrzmy raz jeszcze na przykład z plikiem index.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 niewygodne zwłaszcza, gdy trzeba je będzie z jakiegoś powodu zmienić.

Zdefiniujmy zatem wszystkie używane ścieżki w pliku index.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ę 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:

error_reporting(1 | 2 | 4 | 8);

Jednak w takiej postaci chyba żaden programista nie potrafiłby odczytać, jaki właściwie poziom raportowania błędów został ustawiony. Zamiast cyfr, można użyć odpowiadające im stałe zdefiniowane przez 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 oznacza która wartość logiczna. 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, czyli kolejne bity liczby. Następnie za pomocą operatora alternatywy bitowej zbudujemy z nich odpowiednią kombinację:

<?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 & SILNIK 
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 & SWIATLA 
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.

Ścieżki oraz include_path

edytuj

Zastanówmy się, skąd PHP wie gdzie szukać naszych plików. Podczas uruchamiania skryptu PHP ustawia mu katalog roboczy na folder, w którym znajduje się wykonywany plik i według niego domyślnie rozpoznaje wszystkie ścieżki względne, czyli takie, które zaczynają się od pojedynczej kropki (katalog bieżący) lub podwójnej (katalog nadrzędny). Załadowanie kolejnego skryptu poprzez require nie ma wpływu na tę ścieżkę! Wracając do naszego przykładu z silnikiem strony oznacza to, że gdybyśmy chcieli w pliku ../includes/dispatch.php załadować jakiś inny plik z tego samego katalogu, powinniśmy podać ścieżkę ../includes/innyPlik.php (względem index.php) zamiast ./innyPlik.php. Jest to jeden z powodów, dla którego nie jest zalecane tworzenie akcji bezpośrednio w katalogu publicznym, np. rejestracja.php, logowanie.php, lecz stworzenie pojedynczego punktu wejścia i wybór akcji poprzez odpowiedni algorytm napisany w PHP. Oprócz tego, ma to również pewien wpływ na bezpieczeństwo i niezawodność, a także umożliwia łatwe zastosowanie mechanizmu mod_rewrite oferowanego np. przez serwer Apache do stworzenia przyjaznych adresów URL.

Pozostaje wciąż pytanie, co się dzieje, jeśli ścieżka nie zaczyna się od pojedynczej lub podwójnej kropki. Gdy jest to ścieżka bezwzględna, czyli zaczynająca się od ukośnika w systemach uniksowych, a od litery dysku w systemach Windows, PHP od razu ładuje plik z podanej lokalizacji i nie wykonuje żadnych innych przeszukań. Ostatnia możliwość to podana od razu nazwa pliku lub katalogu:

require('plik.php');

PHP wykorzystuje wtedy mechanizm include_path. Jest to zwyczajna lista ścieżek oddzielonych średnikami, pod którymi należy szukać danego pliku. PHP sprawdza je po kolei, dopóki nie trafi. Jej domyślna wartość jest zdefiniowana w pliku php.ini i domyślnie będzie mieć wartość w stylu:

.;/sciezka/do/katalogu/publicznego;/sciezka/do/PEAR

W ten sposób PHP na początku sprawdzi zawsze katalog bieżący, później katalog publiczny, a później repozytorium klas PEAR, którym nie będziemy się teraz zajmować. Listę ścieżek można modyfikować z poziomu skryptu PHP przy pomocy funkcji set_include_path() oraz get_include_path(). Utwórz dwa pliki o nazwie wczytany.php. Jeden z nich umieść w katalogu publicznym, drugi w katalogu nadrzędnym. Ich kod niech będzie następujący:

<?php
echo __FILE__;

__FILE__ jest specjalną stałą, która zawsze zawiera ścieżkę bezwzględną pliku, w którym się ona znajduje. Dzięki niej będziemy wiedzieć, który z plików się faktycznie załadował. Dodajmy także plik index.php, który będzie ładować jeden z nich:

<?php
require('wczytany.php');

Uruchommy ten skrypt i zobaczmy, jaka ścieżka została wypisana na ekranie. Powinien zostać załadowany plik leżący w katalogu publicznym, ponieważ include_path nakazuje w pierwszej kolejności przeszukać właśnie jego. Teraz zmodyfikujmy include_path:

<?php
set_include_path('..'.PATH_SEPARATOR.'.');
require('wczytany.php');

Tym razem załadowany został plik z katalogu nadrzędnego, ponieważ po modyfikacji PHP w pierwszej kolejności szuka właśnie tam. Usuńmy teraz nadrzędny wczytany.php. PHP załadował plik z katalogu publicznego, gdyż w nadrzędnym nie znalazł tego, czego szukał. Ten eksperyment powinien pokazać istotę działania include_path. Użyty w kodzie identyfikator PATH_SEPARATOR to kolejna stała zawierająca aktualny separator ścieżek.

Oczywiście pojawia się pytanie, który sposób jest bardziej wydajny. Include_path generuje dodatkowy narzut wraz ze wzrostem liczby ścieżek, które musi sprawdzać, dlatego gdy znamy strukturę naszej strony, lepiej posługiwać się ścieżkami bezwzględnymi bądź względnymi:

<?php
// wczytuj zawsze z bieżącego katalogu
require('./wczytany.php');

Include_path przydaje się w sytuacjach, gdy potrzebna nam większa elastyczność lub chcemy skorzystać z bibliotek PHP udostępnianych na serwerze dla ogółu (np. wspomniane już repozytorium PEAR, które zazwyczaj umieszczane jest w katalogu instalacyjnym PHP). Dzięki temu nasz skrypt nie musi wiedzieć, gdzie fizycznie znajdują się odpowiednie pliki na serwerze, lecz po prostu je wczytuje, a interpreter zajmuje się resztą.

Będąc przy temacie ścieżek warto zwrócić uwagę na różnice między systemami operacyjnymi. PHP jest językiem niezależnym od platformy, dlatego skrypty napisane pod Windowsem powinny działać także na serwerze z zainstalowanym Linuksem, ale gdy zaczynamy bawić się plikami, mogą wystąpić problemy. Poniżej zostały zebrane wszystkie najważniejsze różnice, o których trzeba pamiętać, pisząc własne skrypty. Powinny zapamiętać je w szczególności osoby pracujące pod systemem Windows, który bardzo rzadko instaluje się na serwerach WWW.

Własność Windows Linux i inne
Wielkość liter w plikach Nie ma znaczenia Ma znaczenie
Separator katalogów \ /
Korzeń systemu plików Litera dysku /
Podział na dyski Widoczny dla użytkownika Niewidoczny dla użytkownika
Mechanizm uprawnień Tylko po skonfigurowaniu Zawsze

Najwięcej problemów generuje sprawa wielkości liter. Jeśli widzisz, że skrypt działający pod Windowsem nie może znaleźć plików na serwerze Linuksowym, w pierwszej kolejności sprawdź czy jeśli np. zaczynasz w wywołaniach nazwy plików dużą literą, faktycznie tak się zaczynają na dysku. Warto korzystać także z separatora katalogów w stylu uniksowym, czyli ukośnika w prawo. Jest on rozpoznawany także przez Windows, natomiast ukośnik w lewo przez systemy uniksowe - nie. PHP przechowuje odpowiedni ukośnik charakterystyczny dla systemu, na którym pracuje, w stałej DIRECTORY_SEPARATOR.

Referencje

edytuj

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. Modyfikując jedną zmienną, zmiana automatycznie będzie widoczna w drugiej.

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);  
}
  
var_dump($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 var_dump() 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)
{
   // treść
}

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

Pierwszy zapis spowoduje błąd, gdyż nie przekazaliśmy wartości w postaci zmiennej.

W naszych skryptach powinniśmy unikać stosowania referencji. Prawidłowe ich zastosowanie to sytuacja, w której potrzebny jest nam alias na zmienną, czyli de facto kilka zmiennych prowadzących do tej samej wartości. Bardzo złą praktyką jest wykorzystywanie referencji do optymalizacji skryptu. Nie wykonujemy wtedy kopiowania, ale PHP implementuje specjalny algorytm, który opóźnia wykonanie kopii do momentu, gdy jest ona naprawdę potrzebna. Jeżeli zrobimy przypisanie $a = $b i nie wykonamy żadnej operacji, która uzasadniałaby utworzenie kopii, PHP nie będzie tracić czasu na jej robienie!

Należy wystrzegać się także wywoływania funkcji ze zmuszaniem do przekazywania referencji, gdy funkcja sobie tego wyraźnie nie życzy: funkcja(&zmienna) - zapis taki był poprawny jeszcze w PHP 4, lecz w PHP 5 został wycofany i generuje ostrzeżenie.

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ą, która jednak prowadzi do dość dużego spadku wydajności, trudnych do wykrycia błędów, a nawet problemów z bezpieczeństwem. PHP jest tak zaprojektowany, że praktycznie nigdy nie potrzeba w nim używać eval. Oto dwa przykłady:

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

Pokazane to zostało w przykładach powyżej.

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.