Řízené vkládání zdrojových kódů

7. března 2003

Čím větší projekt, tím je implementace složitější. Je potřeba komunikovat s databázovým serverem, FTP, IMAP, zpracovávat XML a jiné vstupy. V tomto článku se budu zabývat otázkou, jak správně a přehledně řídit zařazování kódu v rozsáhlém PHP skriptu. Využiji přitom metodu objektového programování, kdy vývojář píše jednotlivé komponenty a udržuje si pořádek stylem „co objekt, to samostatný soubor“.

Máme-li určen cíl, pojďme si hned ukázat techniku, jak řídit řazení PHP kódu v programu. Já osobně v PHP programuji objektově. Každý objekt píši do vlastního souboru, udržuji ho pak v určeném adresáři. Uvažujme o hlavním skriptu index.php. Chci-li vytvořit objekt A (instanci třídy A), musí interpret PHP znát jeho definici třídy. Pak bych mohl psát:

<?php
$A = new class_a();
?>

Definici třídy class_a() uloženou v souboru class_a.php, musím načíst jedním z příkazů:

<?php
include ‚class_a.php‘;
require ‚class_a.php‘;
include_once ‚class_a.php‘;
require_once ‚class_a.php‘;
?>

Rozdíl mezi těmito příklady je dostatečně popsán v manuálu PHP. Nyní se pojďme podívat, jak by vypadal kód programu, pokud bychom používali více objektů:

<?php
// index.php
require_once ‚class_a.php‘;
require_once ‚class_b.php‘;
$a = new class_a();
$b = new class_b();
?>
<?php
// class_a.php
require_once ‚class_c.php‘;
class a {
  …
}
?>

V tomto jednoduchém příkladu potřebujeme pracovat s objekty A a B. Objekt A ještě pro svou práci využívá objekt C. Říkáme tedy, že funkčnost objektu A je závislá na objektu C. Tento příklad je přehledný, v adresáři existují vedle souboru index.php i class_a.php, class_b.php a class_c.php. Soubor index.php může být v tomto případě výchozím dokumentem webu, ovšem proč v tomto adresáři vedle něj existují class_a.php, class_b.php a další? Tyto soubory by měly patřit mimo část _public_ našeho webu. Přesuňme je tedy mimo tuto oblast a podívejme se na kód příkladu nyní:

<?php
// index.php
require_once ‚../../../htprivate/code/class_a.php‘;
require_once ‚../../../htprivate/code/class_b.php‘;
$a = new class_a();
$b = new class_b();
?>
<?php
// class_a.php
require_once ‚../../../htprivate/code/class_c.php‘;
class a {
  …
}
?>

Jak dlouho by nám asi trvalo vyznat se po týdnu v tomto kódu a jak dlouho by to mohlo trvat u rozsáhlého skriptu? Vyvíjíme přece webovou aplikaci, každou chvíli se něco mění a ačkoli máme připravenu analýzu, musíme občas přepisovat kód, který jsme napsali již před dvěma měsíci… Pak se v takových odkazech na soubory těžko orientujeme a odvádíme pozornost jinam. U většího projektu určitě stojí za to situaci řešit. Princip mého řešení je blízký pointrům z C. Nebudu při každé potřebě určitého kódu psát:

<?php
include ‚cesta/jmenosouboru.php‘
?>

Raději se vždy odkáži na funkci, která to zařídí:

<?php
// index.php
require_once ‚../../../htprivate/project/code/setup.php‘;
load_a();
load_b();
$a = new class_a();
$b = new class_b();
?>
<?php
// class_a.php
load_c();
class a {
  …
}
?>

Nevolám už require_once() (na začátku skriptu ručně načtu jen setup.php), ale volám „načti třídu A!“, „načti třídu B!“. V programu potřebuji načíst deklaraci tříd a je mi na tomto místě přece jedno, odkud (z jakého souboru) se třída A (respektive třída B) načte. Obě funkce využívají objekt CODE MANAGERA:

<?php
class code_manager {
  var $DIRECTORY_ROOT         = “;
  function code_manager($root = NULL) {
    if (isset($root)):
      $this->set_dirroot($root);
    endif;
  }
  function set_dirroot($root) {
    $this->DIRECTORY_ROOT = $root;
  }
  function prequire($path, $relative = NULL) {
    if (empty($relative)):
      require $this->DIRECTORY_ROOT.$path;
    else:
      require $path;
    endif;
  }
  function prequire_once($path, $relative = NULL) {
    if (empty($relative)):
      require_once $this->DIRECTORY_ROOT.$path;
    else:
      require_once $path;
    endif;
  }
  function test() {
    echo ‚<pre>‘;
    print_r(get_required_files());
    echo ‚</pre>‘;
  }
}
?>

Metodou test() si potom můžeme ověřit, jaké soubory byly načteny a odladit chyby.

<?php
// setup.php
$code_mng_str = ‚/Program Files/Apache Group/Apache/htprivate/general/‘;
$mycode_mng_str = ‚/Program Files/Apache Group/Apache/htprivate/project/‘;
require_once ($code_mng_str.’code/class.codemanagement.php‘);
$code_manager = new code_manager($code_mng_str);
$mycode_manager = new code_manager($mycode_mng_str);
$mycode_manager->prequire_once(‚code/general.php‘);
load_template();
?>

Právě na tomto jediném místě se určí cesty do adresářů, kde udržujeme kód PHP programů, knihovny funkcí, deklarace tříd. Objekty CODE MANAGERA lze inicializovat i relativně položenou cestou. Musíme přitom pamatovat, že vždy relativně k veřejně uloženému programu. V souboru, který jsem pojmenoval setup.php rovnou includuji soubor general.php a nástroj pro práci se šablonami. Pokud vím, že tento nástroj používám ve všech programech, nebudu se bát načíst kód již zde:

<?php
// general.php
function load_a() {
  global $mycode_manager;
  load_c();
  $mycode_manager->prequire_once(‚code/class_a.php‘);
}
function load_b() {
  global $code_manager;
  $code_manager->prequire_once(‚code/class_b.php‘);
}
function load_c() {
  global $code_manager;
  $code_manager->prequire_once(‚code/class_c.php‘);
}
function load_template() {
  global $code_manager;
  $code_manager->prequire_once(‚code/class.template.php‘);
}
?>

Nyní si to zopakujme. V index.php nejprve načítám setup.php. Zde je uložena absolutní cesta k adresáři general ($code_manager) a project ($mycode_manager). Pro oba adresáře vytvářím instanci objektu CODE MANAGERA. Do adresáře general ukládám všeobecně často užívané deklarace tříd, do adresáře project ukládám takové deklarace, které souvisí jen s daným projektem (může to být například chat, weblog, ankety atd.).

Práci a orientaci dále ulehčí funkce jako load_a(), load_b(), load_c() či load_template(). V případech, kdy jeden objekt využívá druhý a tento je dále závislý na nějakém dalším, se tímto dosáhne elegantního kódu.

Vložení kódu je možné několika způsoby (include, include_once, require, require_once) – v tomto případě se volil require_once(). Analogicky si můžeme definovat metody pinclude() i pinclude_once().

Jelikož se vložení (require_once) kódu provádí v objektu code_manager (přesněji metodou prequire_once), plyne z toho jedno omezení. Nemůžeme totiž například v souboru class_a pracovat s globální proměnnou (objektem, polem apod.) při absenci odkazu na $GLOBALS[]. Z mého pohledu z toho plyne cesta k důslednějšímu OOP. Podívejte se například na soubor db_connect.php (tento kód při načtení CODE MANAGEREM absolutně ztratí smysl):

<?php
$server_name = „localhost“;
$db_user = „root“;
$db_pass = „sa“;
$db_name = „jmeno_databaze“;
?>

A nyní tentýž soubor db_connect.php (nyní při použití CODE MANAGERA.. poté pracujeme s objektem $db_connect_info):

<?php
class db_connect_info {
  var $server_name = „localhost“;
  var $db_user = „“;
  var $db_pass = „“;
  var $db_name = „sipky_vyvoj“;
}
global $db_connect_info;
$db_connect_info = new db_connect_info();
?>

Tak to je řízení vkládání kódu v PHP podle mého. Zdá se vám tato technika naprosto zbytečná či složitá? Nebo používáte zaběhnutou techniku z příruček pro začátečníky, případně máte svou vlastní? Podělte se o své zkušenosti v diskusi.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *