This post comes from the first version of this blog.
Please send me an email if anything needs an update. Thanks!

Programując w dowolnym współcześnie wykorzystywanym języku na pewno zdarzyło Wam się korzystać z tzw. flag. Flaga, to nic innego niż stała wartość zapisana pod odpowiednim identyfikatorem. Jedną z szerzej stosowanych możliwości jest użycie ich jako komunikatów - numerów lub identyfikatorów np. błędów. W dzisiejszym wpisie pokażę pewien ciekawy sposób na przekazywanie bardziej zrozumiałych informacji dla użytkownika końcowego na podstawie tych właśnie elementów języka.

Wstęp: Komunikaty o błędach.

W języku C i C++ stałe mogły być definiowane na wiele sposobów. Mogliśmy wykorzystać modyfikator const, aby oznaczyć zmienną jako "niezmienną" [co oczywiście dało się na wiele sposobów obejść - jak to w C ;]], lub uruchomić mechanizm preprocesora i zdefiniować potrzebne nam stałe za pomocą makra #define. W języku PHP także mamy kilka możliwości definiowania stałych. Są to: Rozważmy więc przykładowy listing:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
define('WARNING', 0xF0);
define('FATAL_ERROR', 0xFF);

// ...

if($value < 0)
 {
 echo 'Error: '.FATAL_ERROR.' in '.__FILE__.':'.__LINE__;
 }

// Error: 255 in index.php:32

Na górze znajdują się definicje stałych oznaczających kody poszczególnych błędów, a następnie przykładowy kod “wyrzucający” błąd na ekran. Ostatnia linijka zawiera potencjalny wygląd takiego błędu w wyniku działania powyższego skryptu. Każdy programista z większym doświadczeniem zada sobie od razu pytanie - a co, jeśli będzie więcej rodzajów błędów? Czy będę musiał pamiętać nazwy wszystkich stałych? Oczywiście nie, ponieważ można to wykonać w nieco lepszy sposób, np.:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Errors
 {
 const WARNING = 0xF0;
 const FATAL = 0xFF;
 }

// ...

if($value < 0)
 {
 echo 'Error: '.Errors::FATAL.' in '.__FILE__.':'.__LINE__;
 }

// Error: 255 in index.php:32

W tym momencie przechowujemy listę kodów błędów w jednym miejscu, a więc mamy nad nimi o wiele większą kontrolę. Możemy też użyć klasy Errors jako referencji dla istniejących rodzajów błędów tak, aby każdy programista w zespole był świadomy tego, co potencjalnie go czeka. Nadal jednak nie zmienił się sposób wyświetlania komunikatu dla użytkownika końcowego. Czy można coś z tym zrobić?

PHP: Lista stałych zdefiniowanych w klasie.

Oczywiście, że można! ;] Załóżmy, że będziemy przechowywać flagi jako stałe zawarte w odpowiedniej klasie. Załóżmy ponadto, że nazwy naszych flag będą nieco bardziej informatywne, niż podane wyżej. ;] Poniższy listing definiuje listę kodów komunikatów:
1
2
3
4
5
6
7
8
class Messages
 {
 const NOT_ENOUGH_PRIVILEGES = 0x00;
 const FAILED_TO_OPEN_CONNECTION = 0x01;
 const UNCAUGHT_EXCEPTION = 0x02;
 // ...
 const UNKNOWN_ERROR = 0xFF;
 }

W tym momencie następuje “magia”. Otóż, korzystając z opisywanej przeze mnie wcześniej refleksji w języku PHP możemy odczytać także listę stałych zdefiniowanych w danej klasie. Odbywa się to w następujący sposób:

1
2
3
$r = new ReflectionClass('Messages');
$constants = $r->getConstants();
var_dump($constants);

Wynik:

array(4) {
  ["NOT_ENOUGH_PRIVILEGES"]=>
  int(0)
  ["FAILED_TO_OPEN_CONNECTION"]=>
  int(1)
  ["UNCAUGHT_EXCEPTION"]=>
  int(2)
  ["UNKNOWN_ERROR"]=>
  int(255)
}

Prawda, że proste? Ze względu na fakt, że będziemy chcieli się odwołać do numerów komunikatów, zamiast ich nazw, skorzystamy jeszcze z dobrodziejstwa funkcji array_flip(), która zamienia klucze tablicy z jej wartościami. Możemy teraz zmienić nieco kod z przykładu z klasami, otrzymując finalnie następujący listing:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Messages
 {
 const NOT_ENOUGH_PRIVILEGES = 0x00;
 const FAILED_TO_OPEN_CONNECTION = 0x01;
 const UNCAUGHT_EXCEPTION = 0x02;
 // ...
 const UNKNOWN_ERROR = 0xFF;
 }

// ...

$r = new ReflectionClass('Messages');
$constants = array_flip($r->getConstants());

// ...

if($value < 0)
 {
 echo 'Error: '.$constants[Messages::NOT_ENOUGH_PRIVILEGES].' in '.__FILE__.':'.__LINE__;
 }

// Error: NOT_ENOUGH_PRIVILEGES in index.php:32

Oczywiście do niniejszego błędu można jeszcze dodać jakiś własny komentarz, aczkolwiek to pozostawiam już Waszej inwencji. Myślę, że pokazane dzisiaj informacje przyczynią się do zwiększenia Waszych możliwości korzystania z mechanizmów języków interpretowanych i wyciśnięcia z nich “ostatnich soków” [a jest co wyciskać ;]]. Do zobaczenia w kolejnym wpisie!