× Aktuálně z oboru

Programátoři po celém světě dnes slaví Den programátorů [ clanek/2018091300-programatori-po-celem-svete-dnes-slavi-den-programatoru/ ]
Celá zprávička [ clanek/2018091300-programatori-po-celem-svete-dnes-slavi-den-programatoru/ ]

Metatable – ukládáme a vybíráme data v PHP

[ http://programujte.com/profil/10927-jakub-kulhan/ ]Google [ ?rel=author ]       [ http://programujte.com/profil/14523-martin-simecek/ ]Google [ ?rel=author ]       1. 6. 2009       21 684×

Seznamte se s metatable – řešením pro jednoduché ukládání a vybírání strukturovaných dat v PHP napsaným v čistém PHP.

Pokud pracujete s PHP, existují v základu dvě možnosti, jak ukládat data. První z nich je pracovat se soubory, druhou použít nějakou databázi. Vývoj programátora v PHP se většinou ubírá tím směrem, že nejdříve používá první způsob (soubory) a poté své znalosti a dovednosti obohatí o druhý způsob (databáze).

Výhoda ukládání do souborů tkví v tom, že je (v závislosti na datech v nich ukládaných) jednoduché se soubory pracovat. Ale to je z výhod asi tak všechno. Jestliže se pro práci se soubory rozhodneme používat nějaké ty základní funkce jako fopen(), fread(), fwrite() atd. a nebudeme řešit případy, kdy je soubor otevřen z více instancí PHP interpretu, brzo narazíme. A to tvrdě. Problém je v tom, že věci selžou tehdy, když se to nejméně hodí, a poté je už pozdě. Server spadne zrovna v půlce zápisu důležitého souboru, dvě instance interpretu se snaží zapisovat na stejné místo v souboru apod. Buď v souboru budou data v nekonzistentním stavu, takže se aplikace bude chovat divně, nebo budou naprosto znehodnocena. Ani jedno není dobré. Tématem atomicity operací se soubory se zabývá hodně článků, proto odkazuji na Google.

Databáze naopak více přístupů ke stejným datům řeší elegantně, pád databázového serveru by také neměl znamenat, že všechna data ztratíme. Starost o toto všechno místo nás má na práci databáze. A seznam pokračuje – řazení, výběry jen určitých dat, práce s nimi, počítání… Toto všechno databáze řeší. Nutno poznamenat, že databáze nemusí být pouze typu klient-server, existují i souborové – viz SQLite. Ale pro některé druhy práce s daty by bylo cpát je databázi jako jít s kanónem na vrabce. Taky všechny databáze, které jsou dostupné pro PHP, potřebují nějaké rozšíření, žádná databáze nemá rozhraní pro přístup k sobě v jádře PHP (opravte mě, pokud se mýlím). Je potřeba říci, že většina, ne-li všechny, hostingů nabízející PHP k němu nabízí i databázi, a tudíž ho provozuje s potřebnými rozšířeními.

Disclaimer: Autor článku je hlavním vývojářem metatable.

A co je tedy metatable? Jako je výše zmiňovaná SQLite řešením na pomezí mezi „klasickou“ databází (za „klasickou“ považujme databázi s klient-server architekturou) a soubory, které se spíše blíží k databázím, tak metatable je řešení na pomezí databází a souborů tíhnoucí spíše k souborům. Jaký je výsledek? metatable nabízí ukládání dat do souborů atomicky, transakčně (jako databáze), ale zase můžete zapomenout na nějaké promyšlené řazení, počítání atd. metatable je opravdu jen o ukládání a získávání dat zpět. Také nepotřebuje žádná rozšíření – je napsána v čistém PHP. Název metatable pochází z toho, že první úmysl byl ji použít na ukládání metadat. Část table je inspirována tím, jakým způsobem jsou data adresována – viz Data v podání metatable.

Sem s ní

metatable je uvolněna pod svobodnou licencí (nová BSD licence) a hostovaná na Google Code [ http://code.google.com/p/metatable/ ]. Můžete ji získat jednak stažením vytvořeného archivu se zdrojovým kódem [ http://metatable.googlecode.com/files/metatable-f87d747066.tar.gz ] a/nebo přímo z repozitáře. metatable využívá distribuovaný verzovací systém Mercurial [ http://www.selenic.com/mercurial/wiki/ ]. Pokud ho máte v systému nainstalovaný, naklonování repozitáře spočívá ve spuštění jednoduchého příkazu:

$ hg clone http://metatable.googlecode.com/hg/ metatable

Ať už v archivu nebo repozitáři najdete soubor metatable.php, který obsahuje veškerý potřebný kód (opravdu, jen jeden soubor), a adresář examples/, který v sobě ukrývá některé příklady.

Data v podání metatable

Data v souborech jsou uložena různě, jsou využívány rozličné formáty a každý se hodí pro něco jiného. Databáze, relační databáze, zase mají své relace a vztahy mezi nimi. (Je za tím hodně akademických výzkumů a mnoho začátečníků se v tom ztrácí.) A metatable všechno ukládá do velké hashmapy (můžete znát též jako hash, mapa, dictionary (česky slovník), table (česky tabulka – termín používaný v jazyku Lua [ http://lua.org/ ]), ale u PHP asi nejpoužívanější název – asociativní pole). Ve své podstatě se jedná o strukturu uložení dat, kdy ke každému klíči je přiřazena jedna hodnota. (Přičemž klíč je u metatable jedinečný.)

metatable je jedna velká hashmapa. Jako klíč je použita dvojice řetězců, označujme je jako řádek (row) a sloupec (column, col). metatable tedy mapuje tuto dvojici na hodnotu – (row : string, col : string) → value : any. Hodnota může být několika typů. metatable nyní rozpoznává a umí uložit nativní typy PHP řetězec [ http://php.net/manual/en/language.types.string.php ] (string), celočíselný typ [ http://php.net/manual/en/language.types.integer.php ] (integer) a logický typ [ http://php.net/manual/en/language.types.boolean.php ] (boolean). Nyní lze ostatní typy ukládat v serializované podobě pomocí typu string, protože metatable řetězce nijak neinterpretuje.

Práce s metatable

Rozhrazní metatable by snad nemohlo být jednodušší. Po většinu času si vystačíte se čtyřmi základními metodami – pro otevření metatable souboru, pro čtení dat, pro jejich zápis (či přepis, či vymazání) a pro uzavření souboru. O všechno ostatní se stará vnitřní implementace metatable a vy už nemusíte.

Základem tedy je vytvořit si nějakou takovou tabulku:

<?php
require_once "metatable.php";

$table = metatable::open("table.metatable");

Řetězec za require_once si upravte tak, aby směřoval na soubor s kódem metatable. Příklad udělá jediné – otevře soubor table.metatable jako metatable tabulku. (Pokud soubor neexistuje, bude při uložení tabulky vytvořen.) Ovšem ne vždycky se to musí podařit – mohou být špatně nastavena práva k souboru, nemusí se vůbec jednat o metatable soubor apod. Proto je potřeba kontrolovat, co tato statická metoda vrací. Možnosti jsou dvě: úspěšně vytvořenou instanci metatable, nebo FALSE.

metatable::open() příjímá jako druhý nepovinný argument celé číslo, které specifikuje některé věci týkající se vytvářené instance. Jedná se o bitovou kombinaci příznaků, které má instance mít. V době psaní článku existují čtyři příznaky:

  • READONLY – tabulka bude otevřena pouze pro čtení (defaultně vypnuto)
  • READWRITE – otevřeno pro čtení i zápis (zapnuto)
  • STRINGS_GC – garbage collection řetězců (zapnuto)
  • AUTOCLOSE – automatické uzavření a uložení tabulky při garbage collection instance metatable (vypnuto)

Řekněme, že chceme otevřít tabulku s tím, že z ní budeme moci číst a do ní zapisovat, provede při zavírání garbage collection řetězců a bude se uzavírat automaticky při rušení instance:

$table = metatable::open("table.metatable", 
    metatable::READWRITE | metatable::STRINGS_GC | metatable::AUTOCLOSE);

Oproti tomu pro otevření pouze pro čtení můžeme použít:

$table = metatable::open("table.metatable", metatable::READONLY);

Příznak READONLY pozitivně ovlivňuje výkon, pokud z tabulky opravdu potřebujeme jenom číst. Taky je chování metatable trochu jiné. Pokud předáme příznak READONLY, ostatní postrádají smysl. A byl-li by předán READWRITE a READONLY zároveň, metoda metatable::open() se ani nepokusí otevřít soubor a rovnou vrátí FALSE. Také očekávejte jiné chování při neexistenci souboru. Pokud soubor není v souborovém systému a my ho chceme otevřít pouze pro čtení, metatable::open() navrátí FALSE, zatímco v READWRITE stavu vše proběhne (pokud se nenaskytne nějaká jiná překážka) v pořádku a soubor bude vytvořen při ukládání.

Když máme vytvořenou instanci, můžeme se vrhnout na zadávání dat. O to se stará instanční metoda set(). Přijímá tři argumenty, kde první dva určují právě dvojici (row, col), na kterou se namapuje třetí parametr – tedy hodnota. metatable má omezení, které určuje, že délka názvu řádku ani sloupce nesmí přesáhnout 124 bytů (znaků – v jednobytových kódováních) a názvy nesmí obsahovat znak s ASCII kódem 0. Pokud bude řetězec delší, bude oříznut a vy se o tom nedozvíte, takže pozor. Následující kód přiřadí dvojici ("row", "col") postupně různé hodnoty:

// 1
$table->set("row", "col", 0);
// 2
$table->set("row", "col", TRUE);
// 3
$table->set("row", "col", "hello, world!");
// 4
$table->set("row", "col", NULL);
// 5

Instanční metoda get() narozdíl od set() data nemění, ale pouze vybírá, a tak může být použita, pokud je tabulka otevřena s příznakem READONLY. Argumenty jsou stejné, až na třetí, který get() nemá. Pro získání dvojice ("row", "col") by get() vypadal následovně:

$table->get("row", "col");

Návratová hodnota je dvourozměrné pole, kde první dimenze je řádek, druhá sloupec. Konkrétní obsah tohoto pole bude záviset na tom, jaká data zrovna tabulka obsahuje. Jak by vypadal v jednotlivých částech našeho kódu se set()? var_dump() [ http://php.net/var_dump ] nám to ozřejmí:

  1. array (0) {
    }
    
  2. array(1) {
      ["row"]=>
      array(1) {
        ["col"]=>
        int(0)
      }
    }
    
    
  3. array(1) {
      ["row"]=>
      array(1) {
        ["col"]=>
        bool(true)
      }
    }
    
  4. array(1) {
      ["row"]=>
    
      array(1) {
        ["col"]=>
        string(13) "hello, world!"
      }
    }
    
  5. array (0) {
    }
    

Je vidět, že nastavíme-li hodnotu na NULL, záznam bude vymazán.

get() také podporuje žolíka * zastupujícího jeden a více znaků. Může být použit jak v názvu řádku, tak sloupce. Zavoláním $table->get("*", "*"); vybereme všechna data z tabulky.

Chceme-li práci s tabulkou ukončit, prostě ji uzavřeme:

$table->close();

V závislosti na tom, s jakými příznaky je metatable::open() zavoláno, proběhnou potřebné úkony.

Posledními dvěma veřejnými metodami metatable jsou index() a unindex(). Index je předvypočítané umístění nějaké skupiny záznamů v tabulce. Indexuje se pomocí začátku názvu řádku. Pokud tedy např. často přistupujeme k záznamům na řádku "row", můžeme si pro ně vytvořit index – $table->index("row");. Což nám sníží nutnost číst soubor a hledat, kde se který záznam nachází. unindex() přijímá stejné argumenty, akorát index smaže.

K čemu to je?

Už víte, co to metatable je, jak se s ní pracuje, ale asi vás napadají otázky typu: k čemu to ksakru je? A není to znovuvynalézání kola? Nejdříve odpovím na tu druhou. Kolik takovýchto kol jste již viděli? Já zatím ani jedno.

Uplatnění metatable je tam, kde jsou soubory na ukládání krátké a databáze zbytečně velké. Soubory jsou ideální na ukládání velkých objemných dat, se kterými nepotřebujeme nic dělat nebo by bylo těžké s nimi něco provádět v databázi (binární data). Řekněme, že stavíme webovou galerii. Fotky budou ukládány v souborech. Ale to povětšinou nestačí, je potřeba mít možnost k nim přiřadit ještě různé popisky, nadpisy, kdo to fotil, proč to fotil, kde to fotil atp. – prostě metadata, aneb data popisující data.

Jednou a velice dobrou možností je použít databázi. Ale pro nějakou malou galérku pro mě a pár kamarádů je to přeci jenom zbytečné. Teď nastupuje na řadu metatable. Se svou dynamicky rostoucí hashmapou (a to jak do délky, tak do šířky) je na řešení dobrým kandidátem.

Mějme kupř. metatable fotky.metatable. Jako název řádku bude vždy nějaký jednoznačný identifikátor fotky. Tak k nějaké takové fotce přiřaďme soubor, ve kterém se nachází: $table->set($id, "file", "/fotky/fotka.jpg");. A můžeme pokračovat s dalšími sloupci jako název fotky, datum vytvoření atp. K výběru všech dat o nějaké fotce pak zavoláme $table->($id, "*");.

A co takové štítkování fotek? Není problém. $table->set($id, "stitek:programovani", TRUE); přiřadí fotce štítek (pomocí „přiřazení“ NULL by se štítek odebral). Seznam ID fotek s jedním štítkem získáme skrz array_keys($table->get("*", "stitek:programovani"));.

Závěr

metatable se nesnaží nahradit ani ukládání dat do souborů, ani do databáze. Berte ji jako doplněk k předchozím dvěma. Je napsána čistě v PHP, což sráží její výkon oproti kompilovaným rozšířením, ale zase se můžete spolehnout, že půjde i bez jakéhokoli z nich. Formát souboru by se v budoucnu neměl nijak dramaticky měnit, tudíž tabulku, kterou vytvoříte dnes, dokážete přečíst i za pár let.

Výzva na konec. Zaujala-li vás metatable, jedná se o open-source projekt, tudíž můžete sami přispět – hlaste chyby, ovlivněte budoucí vývoj!


Článek stažen z webu Programujte.com [ http://programujte.com/clanek/2009053100-metatable-ukladame-a-vybirame-data-v-php/ ].