Projektowanie stron WWW od podszewki

Kursy mojej produkcji

3.1 Tablice - teoria

Często można spotkać się z opinią, że tablice w PHP są jak uporządkowane listy. Osobiście wolę powiedzieć, że jest to odpowiednik zeszytowej tabelki, o czym przekonacie się za chwile. Bardziej fachowe określenie array mówi, że tablice są zmiennymi zbiorczymi, które służą do grupowania elementów mających cechy zmiennych. Tablice charakteryzuje fakt, że posiadają odpowiednie klucze i wartości do niej przypisaną. Klucz interpretujemy w taki sposób, że jest to identyfikator, przez który możemy odwołać się do konkretnego elementu tablicy. W wartości natomiast trzymamy dane/informacje, które są nam potrzebne. W języku PHP rozróżniamy kilka "typów" tablic. Pierwszą z nich są tablice jednowymiarowe, drugą tablice dwuwymiarowe i trzecią rozpoznawalną opcją są tablice wielowymiarowe.

Tablice w PHP a inne języki programistyczne

Tablice w PHP mają bardzo ważną zaletę w porównaniu do większości języków języków kompilowanych. Po pierwsze parser PHP pozwala na tworzenie tablice z różnymi typami kluczy i wartości. Czyli de facto klucze mogą być typem np. string, int. Ta właściwość tyczy się także wartości. Nie ma ograniczeń w tym jakiego typu mają być. Sprawa w większości innych języków ma się z goła inaczej. Mówiąc o typowej array można dostrzec pewne limity. To znaczy np. wartości zdefiniowanej tablicy muszą mieć jednakowy typ. Gdy wystąpi nieprawidłowość to wtedy zaczynają się schody. Przykładowo w C++ definiowanie tablicy wygląda następująco:
Kod:
int tablica[10];
tablica[9] = 123;
W przypadku próby zdefiniowana wartości dla tablicy z indeksem 10 mogą pojawić się dwa warianty. Po pierwsze mądrzejsze języki takie jak np. C# odpowiednio poinformują o niefortunnej sytuacji. Drugą opcją jest efekt tzw. wycrashowania się aplikacji, bo tak naprawdę odwołując się np. do tablica[10] oznacza odwołanie się do 11 elementu w zdefiniowanej 10 elementowej tablicy. Oczywiście crash nie musi wystąpić od razu, ale nieprawidłowe nadpisanie niezarezerwowanej pamięci skutkuje błędami w przyszłości. Dlatego właśnie uważam, że do programowania w takich językach powinni być zatrudniani jedynie "koksy", które doskonale operują na tym języku.

Korzystając z okazji powiem także o drugiej sprawie. Oczywiście trzeba rozgraniczyć pewną kwestię. Istnieją dedykowane rozwiązania umożliwiające przekształcanie kodu skryptowego na kod maszynowy, co w rezultacie daje duże możliwości i ciągnie za sobą możliwość optymalizacji i przyspieszenia działania samych skryptów. Wygląda to mniej więcej tak, że po kompilacji skryptu określona "stała" część staje się zdefiniowana, co niejako odciąża prace parsera w analizowaniu i przekładaniu kodu PHP na odpowiednie rozkazy.

Jednowymiarowe tablice zmiennych

Najprostszym przykładem są jednowymiarowe, które posiadają klucz i wartość.
Kod:
<?php
$osoba = array('imie' => 'Stefan''nazwisko' => 'Kowalski''wiek' => 27); 
?>
Jak zauważyliście użyte zostało array(), które jest konstrukcją językową. To co znajduję się przed => nazywamy kluczem lub indeksem natomiast co po tym jest wartością. Poszczególne elementy tablicy rozdzielamy , czyli przecinkiem. Często pojawiają się sytuacje, w których index tablicy jest niezdefiniowany i deklaracja takiej tablicy wygląda następująco:
Kod:
<?php
$losowe_liczby = array(712192240);
?>
W takim przypadku, aby odwołać się np. do elementu numer dwa wystarczy użyć $losowe_liczby[1]. Osoby, które wcześniej nie miały do czynienia z programowaniem mogą sobie zadać pytanie dlaczego właściwie 1? Praktycznie we wszystkich językach programowania i informatyce wszystko zaczyna się od sławnego 0. Czyli jeżeli chcemy odwołać się do pierwszego elementu to po prostu używamy $losowe_liczby[0] i dostaniemy w rezultacie liczbę całkowitą o wartości 7, dla przykładu tablicy powyżej rzecz jasna.

Tablice dwuwymiarowe

Kolejnym ciekawym aspektem są tablice dwuwymiarowe. Przypuśćmy, że chcemy stworzyć tablice zwierającą jakąś grupę osób.
Kod:
<?php
$osoby[0] = array('imie' => 'Stefan''nazwisko' => 'Kowalski''wiek' => 27);
$osoby[1] = array('imie' => 'Anita''nazwisko' => 'Nowak''wiek' => 25);
$osoby[2] = array('imie' => 'Rafał''nazwisko' => 'Piotrowicz''wiek' => 24);
?>
Ten sam zapis można przedstawić w nieco innej formie:
Kod:
<?php
// Pierwszy węzeł
$osoby[0]['imie'] = 'Stefan';
$osoby[0]['nazwisko'] = 'Kowalski';
$osoby[0]['wiek'] = 27;
// Drugi węzeł
$osoby[1]['imie'] = 'Anita';
$osoby[1]['nazwisko'] = 'Nowak';
$osoby[1]['wiek'] = 25;
// Trzeci węzeł
$osoby[2]['imie'] = 'Rafał';
$osoby[2]['nazwisko'] = 'Piotrowicz';
$osoby[2]['wiek'] = 24;
?>
Jak zauważyliście użyłem określenia węzeł, które jest dość potoczne jeżeli chodzi o tablice i poszczególne segmenty/elementy większej układanki.

Tablice wielowymiarowe

Wielowymiarowe tablice są dość charakterystycznym motywem niedostępnym w niektórych językach programowania. Na całe szczęście PHP daje nam taką możliwość.
Kod:
<?php
$osoby[0] = array('imie' => 'Stefan''nazwisko' => 'Kowalski''wiek' => 27'stan' => array('zyje' => false), 'dominujące_cechy' => array('wybuchowość''impulsywność'));
$osoby[1] = array('imie' => 'Anita''nazwisko' => 'Nowak''wiek' => 25'stan' => array('zyje' => true'zdrowie' => 90), 'dominujące_cechy' => array('spokoj''przywódczość'));
$osoby[2] = array('imie' => 'Rafał''nazwisko' => 'Piotrowicz''wiek' => 24'stan' => array('zyje' => true'zdrowie' => 30), 'dominujące_cechy' => null); // Mimo krótkiego wieku stan zdrowia oceniany na poziomie 30% ze względu na duże doświadczenia z alkoholem
?>
Oczywiście ten zapis także można przekształcić, aby wyglądał prawie tak jak w przypadku listingu numer 2 w przykładzie dotyczącym tablic dwuwymiarowych.
Kod:
<?php
// Pierwszy węzeł
$osoby[0]['imie'] = 'Stefan';
$osoby[0]['nazwisko'] = 'Kowalski';
$osoby[0]['wiek'] = 27;
$osoby[0]['stan'] = array('zyje' => false);
$osoby[0]['dominujące_cechy'] = array('wybuchowość''impulsywność');
// Drugi węzeł
$osoby[1]['imie'] = 'Anita';
$osoby[1]['nazwisko'] = 'Nowak';
$osoby[1]['wiek'] = 25;
$osoby[1]['stan'] = array('zyje' => true'zdrowie' => 90);
$osoby[1]['dominujące_cechy'] = array('spokoj''przywódczość');
// Trzeci węzeł
$osoby[2]['imie'] = 'Rafał';
$osoby[2]['nazwisko'] = 'Piotrowicz';
$osoby[2]['wiek'] = 24;
$osoby[2]['stan'] = array('zyje' => true'zdrowie' => 30);
$osoby[2]['dominujące_cechy'] = null;
?>
Umyślnie dałem dwa możliwe rozwiązania w poszczególnych węzłach. W pierwszym przypadku chodzi o brak zdefiniowanej wartości dla stanu zdrowia nawet, jeżeli ktoś nie żyje. Ot kwestia oczywista jednak zawsze można dać wartość 0. Żartując można dać także -50 za przekroczenie limitu Smile W drugim przypadku jest zdefiniowanie moim zdaniem niepotrzebnie elementu dominujących cech, które jest null'em. Z punktu widzenia optymalizacji lepiej jest nie zdefiniować niepotrzebnych elementów. Natomiast z punktu czytelności kodu lepiej jest definiować elementy, które i tak nie mają wartości. Decyzja należy do Was albo do Waszego szefa (lub kierownika projektu), który decyduję o takich kwestiach.

Tablice wyliczeniowe i asocjacyjne

W językach programistycznych możemy rozpoznać dwa główne typy tablic.

Tablice wyliczeniowe

Tablice wyliczeniowe, to takie, których indeks zaczyna się od zera i każdy kolejny klucz węzła posiada zainkrementowaną wartość o jeden względem poprzedniego elementu.

Tablice asocjacyjne

Głównym założeniem tego typu tablic jest fakt, że klucze mogą być dowolnego typu. Czyli w rezultacie jeden może być ciągiem znaków, drugi liczbą całkowitą, a trzeci np. liczbą zmiennoprzecinkową. Najprostszy przykład odnośnie tych tablic wygląda następująco:
Kod:
<?php
// Pierwszy węzeł
$pognieciona_tablica[0] = 'Jakaś zawartość';
// Drugi węzeł
$pognieciona_tablica['klucz'] = 'Inna dziwna zawartość';
?>
Czasami możecie spotkać się także z określeniem tablice kombinacyjne bądź mieszane.
W mieszanych głownie chodzi o to, że klucze mogą być wyliczeniowe (numeryczne) i złożone z ciągu znaków.

Operatory tablicowe

Oprócz poznanego wcześniej operatora => istnieje jeszcze szereg innych operatorów, które można stosować wraz z tablicami. Głównym niewymienionym do tej pory operatorem jest []. Jest to tak zwany operator elementu tablicy. Zapis przedstawiony np. na listingu numer 1 dla tablicy jednowymiarowej bądź dwuwymiarowej można nieco skrócić usuwając indeksy kluczy, jeżeli są numeryczne (wyliczeniowe). Po tej zmianie będzie to wyglądać następująco:
Kod:
<?php
$osoby[] = array('imie' => 'Stefan''nazwisko' => 'Kowalski''wiek' => 27); // Pierwszy element
$osoby[] = array('imie' => 'Anita''nazwisko' => 'Nowak''wiek' => 25); // Drugi element
$osoby[] = array('imie' => 'Rafał''nazwisko' => 'Piotrowicz''wiek' => 24); // Trzeci element
?>
Taki sposób jest wygodny, lecz wiąże za sobą pewne problemy, o których wspomnę w dalszej części kursu dotyczącej optymalizacji skryptów, raportowania błędów oraz baz danych.

Proszę zauważyć, że jeżeli wiemy, że w tablicy pierwszy element tablicy będzie zawierać imię, drugi nazwisko, trzeci wiek to w rzeczywistości nie musimy używać kluczy, lecz po prostu zapisać to tak:
Kod:
<?php
$osoby[] = array('Stefan''Kowalski'27); // Pierwszy element
$osoby[] = array('Anita''Nowak'25); // Drugi element
$osoby[] = array('Rafał''Piotrowicz'24); // Trzeci element
?>
Jeżeli chcemy np. sprawdzić wiek osoby numer dwa, czyli Anity Nowak to wystarczy, że wywołamy $osoby[1][2].

Tabela przedstawiająca operatory tablicowe

Operator Nazwa Przykład użycia Wynik
+ Unia $a + $b Zwracana jest tablica zawierająca wszystkie elementy wspólne tablic $a i $b
== Równość $a == $b Zwracana jest wartość true w przypadku, gdy $a i $b mają te same pary kluczy i wartości
=== Identyczność $a === $b Zwracana jest wartość true, gdy $a i $b mają te same pary kluczy i wartości oraz ułożone są w tej samej kolejności
!= Nierówność $a != $b Zwracana jest wartość true w przypadku, gdy $a i $b nie są sobie równe
<> Nierówność $a <> $b Zwracana jest wartość true w przypadku, gdy $a i $b nie są sobie równe
!== Nieidentyczność $a !== $b Zwracana jest wartość true, gdy $a i $b nie są identyczne

Podglądanie zawartości tablic

Mocno prawdopodobne jest, że w trakcie czytania tego segmentu kursu zastanawialiście się w jaki sposób podejrzeć zawartość całej tablicy bądź jej fragmentu. Możliwe, że spróbowaliście "wyrzucić" zawartość przy pomocy znanego Wam echo jednak skutkiem tego był tylko napis Array. Do podglądania tablic stosuję się zazwyczaj dwie funkcje. Wszystko to kwestia upodobań odnośnie prezentacji danych. Pierwszą z nich jest sławne print_r().
Kod:
<?php
$osoby[] = array('imie' => 'Stefan''nazwisko' => 'Kowalski''wiek' => 27); // Pierwszy element
$osoby[] = array('imie' => 'Anita''nazwisko' => 'Nowak''wiek' => 25); // Drugi element
$osoby[] = array('imie' => 'Rafał''nazwisko' => 'Piotrowicz''wiek' => 24); // Trzeci element

print_r($osoby);
?>
Wynik zrzutu zastosowanej powyżej funkcji będzie wyglądać następująco:
Kod:
Array
(
    [0] => Array
        (
            [imie] => Stefan
            [nazwisko] => Kowalski
            [wiek] => 27
        )

    [1] => Array
        (
            [imie] => Anita
            [nazwisko] => Nowak
            [wiek] => 25
        )

    [2] => Array
        (
            [imie] => Rafał
            [nazwisko] => Piotrowicz
            [wiek] => 24
        )

)
Jest także druga funkcja, która moim zdaniem jest częściej używana z powodu posiadania jednego bardzo ważnego argumentu. Funkcja nazywa się var_dump() i nie posiada limitów odnośnie argumentów wrzucanych do wywołania funkcji co w rezultacie czyni ją bardzo potężnym "narzędziem".
Kod:
<?php
$test 'Krzywe bity';

$osoby[] = array('imie' => 'Stefan''nazwisko' => 'Kowalski''wiek' => 27); // Pierwszy element
$osoby[] = array('imie' => 'Anita''nazwisko' => 'Nowak''wiek' => 25); // Drugi element
$osoby[] = array('imie' => 'Rafał''nazwisko' => 'Piotrowicz''wiek' => 24); // Trzeci element

var_dump($test$osoby$osoby[1], $test2);
?>
Jedyną wadą takiego rozwiązania jest fakt, że wynik może być mniej czytelny aniżeli ten pochodzący z print_r(). Moim zdaniem jest to kwestia przyzwyczajenia. Wynikiem powyższego kodu jest taki oto tekst:
Kod:
string(11) "Krzywe bity"
array(3) {
  [0]=>
  array(3) {
    ["imie"]=>
    string(6) "Stefan"
    ["nazwisko"]=>
    string(8) "Kowalski"
    ["wiek"]=>
    int(27)
  }
  [1]=>
  array(3) {
    ["imie"]=>
    string(5) "Anita"
    ["nazwisko"]=>
    string(5) "Nowak"
    ["wiek"]=>
    int(25)
  }
  [2]=>
  array(3) {
    ["imie"]=>
    string(6) "Rafał"
    ["nazwisko"]=>
    string(10) "Piotrowicz"
    ["wiek"]=>
    int(24)
  }
}
array(3) {
  ["imie"]=>
  string(5) "Anita"
  ["nazwisko"]=>
  string(5) "Nowak"
  ["wiek"]=>
  int(25)
}
NULL
Musicie jedynie przetrenować metodę wyświetlania takich danych i to wszystko.

Konkluzja

Uważam, że po tym małym wywodzie zaczniecie doceniać możliwości tablic, co przyczyni się do ich częstego stosowania. Praktycznie każdą tablice można rozrysować na kartce, grafie bądź umieścić ją na tzw. drzewku. Celem takich zabiegów jest analizowanie aktualnej struktury. Ma to głównie miejsce przy analizie większych struktur. Aby nie mieszać Wam w głowach umyślnie użyłem prostych przykładów z imionami. Jeżeli nadal nie wiesz, do czego mogą Ci posłużyć tablice to moją podpowiedź brzmi: np. pliki konfiguracyjne skryptu. Tablice de facto dają nieograniczone możliwości i wielokrotne zagnieżdżanie array() wewnątrz siebie nie stanowi żadnego problemu.