Tutoriale PHP
  Comunitatea PHP Romania

 
Tutoriale PHP
PHP 5, Factory
Google Buzz
PHP 5, Factory
Vezi comentariiDiscuta acest articol (0 comentarii)
TiparesteTipareste
Adauga la favoriteAdauga la favorite

aurelian
O sa incerc pe parcursul mai multor posturi sa explic diferite tehnici introduse in PHP 5 pe care imi place sa le folosesc (sau mi-ar placea).
Motivul?
Data viitoare cand am nevoie de ceva asemanator sa pot gasi repede si cat de cat documentat informatiile necesare, tinand cont ca la un moment dat voi pierde aceste lucruri printr-un colt al hdd-ului.

I. Notiuni

Incerc sa respect cat mai exact regulile publicate sub numele de Coding Standards pe pagina de web PEAR.
Documentatia "inline" a claselor, metodelor si atributelor este foarte folositoare cand citesc codul sursa al unui fisier scris in urma cu 6 luni.
De obicei folosesc phpDocumentor pentru a genera din codul php documentii in mod profesional. Am gasit si un tutorial aici.

II. Intro

Primul exemplu prezentat in aceasta serie se refera la implementarea conceptului de factory in php. Acest exemplu nu este complet si nu poate fi considerat un exemplu standard al ideii de factory.
Pentru a-mi raspunde la intrebarile "Ce este un Factory?", "La ce ma ajuta?", "Este folositor?", "Se merita?" am citit articolul "The Factory Method" de Harry Fuecks.
Am parcurs cu atentie codul postat acolo, si am mai citit si paginile astea ce fac parte din prezentarea "2002 International PHP Conference: Applied OO PHP: APIs, Design Patterns and Useful Objects - Chuck Hagenbuch".
Google mi-a mai adus cateva articole interesante.
Intr-un final, m-am hotarat sa implementez un exemplu scris in java.

Pentru teste, o sa folosesc php CLI si un director numit trace_factory.
Editorul acestui blog nu imi permite sa afisez cod php cu highlight, nici macar sa folosesc succesiunea de caractere <_SEMNUL_INTREBARII_php, asa ca atentie la copy/paste :)
// later, fixed! VIM KIK ASS!.

III. Interfata.

Din documentatia prezentata in manual, printr-o interafa putem creea un obiect care specifica metodele pe care o clasa trebuie sa le implementeze, fara a defini insa comportamentul acestor metode.

Traducerea interfetei din java in PHP 5 arata cam asa:

<?php
/**
* Is the Trace Interface
* It contains common methods for every implementation
* @package test.factory.trace
* @access public
*/

interface Trace {
/**
* Turn on and off debugging
* @param bool debug
*/
public function setDebug($debug);
/**
* Write out a debug message
* @param string message to debug
*/
public function debug($message);
/**
* write out an error message
* @param string message to debug
*/
public function error($message);
}
?>

fisier pe care l-am salvat cu numele de ITrace.php.


Continuand exemplu, vom scrie doua implementari, prima, va afisa mesajele in consola (sau pagina web daca se renunta la CLI) iar cea dea doua, va scrie mesajele intr-un fisier text.

IV. StdoutTrace.php

<?php
include_once('ITrace.php');
/**
* It display the trace to stdout
*
* @package test.factory.trace
* @access public
*/

class StdoutTrace implements Trace {

/**
* debug
* @var bool
* @access private
*/
private $debug;

/**
* Sets the debug
* @param bool debug
* @return void
* @access public
*/
public function setDebug($debug) {
$this->debug = $debug;
}

/**
* It writes a debug message
* only if debug is setted to true
* @param string message
* @return void
* @access public
*/
public function debug($message) {
if($this->debug) {
// only print if debug is true
echo date("d-m-Y H:i:s") . " DEBUG >>>>" . $message . "\n";
}
}

/**
* It writes an error message
*
* @param string message
* @return void
* @access public
*/
public function error($message) {
// always print out errors
echo date("d-m-Y H:i:s") . " ERROR >>>>" . $message . "\n";
}
}
?>

Fisierul l-am salvat cu numele de StdoutTrace.php in acelasi director.

V. FileTrace.php

<?php
include_once('ITrace.php');
/**
* It logs the trace to a file
*
* @todo: implement a custom Exception class
* @package test.factory.trace
* @access public
*/
class FileTrace implements Trace {

/**
* It`s the file handler
* @var handler
* @access private
*/
private $handler;

/**
* Is the file name
* @var string
* @access private
*/
private $file;
/**
* debug
* @var bool
* @access private
*/
private $debug;

/**
* The construnctor
* it opens the file handler
*
* @param string the file name
* @return void
* @access public
*/
public function __construct($file) {
// a real FileTrace would need to obtain the filename somewhere
if( (!is_file($file)) OR (!is_writable($file)) ) {
throw new Exception("The file " . $file . " is not writable,\n
Or it dosent exist in " . __METHOD__ . " at " . __LINE__ . "!\n"
);
}
$this->handler = fopen($file, "a+");
}

/**
* Sets the debug
* @param bool debug
* @return void
* @access public
*/
public function setDebug($debug) {
$this->debug = $debug;
}

/**
* It writes a debug message
* only if debug is setted to true
* @param string message
* @return void
* @throw Exception
* @access public
*/
public function debug($message) {
if($this->debug) {
// only print if debug is true
fwrite($this->handler, date("d-m-Y H:i:s") . " DEBUG >>>> " . $message . "\n");
}
}

/**
* It writes an error message
*
* @param string message
* @return void
* @throw Exception
* @access public
*/
public function error($message) {
// always print out errors
fwrite($this->handler, date("d-m-Y H:i:s") . " ERROR >>>> " . $message . "\n");
}

/**
* Is the Destructor
* just close the handle
* @access public
* return void
*/
public function __destruct() {
if($this->handler===true) {
fclose($this->handler);
}
}
}

Fisierul l-am salvat cu numele de FileTrace.php in acelasi director.


VI. Factory

<?php
include_once('FileTrace.php');
include_once('StdoutTrace.php');

/**
* Is the factory
*
* @access public
* @package test.factory.trace
*/

class TraceFactory {

/**
* It trys to get a trace
*
* @access public
* @return mixed Trace Interface implementation
*/
public static function &getTrace($file='') {
try {
return new FileTrace($file);
} catch (Exception $ex) {
$t = new StdoutTrace();
$t->error("Could not instantiate FileTrace: " + $ex->getMessage());
return $t;
}
}
}
?>

Salvat cu numele de TraceFactory.php.

VII. Ceva Explicatii

Inainte de a merge mai departe, cred ca e timpul sa explic ce am facut pana acum.
Am incercat sa implementez in PHP 5 un model de factory preluat dintr-un exemplu dat in java.
In partea I, am scris o interata ce contine trei metode:
setDebug($debug), care imi va permite sa schimb nivelul afisat de trace (debug sau error),
debug($message), o sa pot afisa un mesaj de debug (bineinteles daca am facut setDebug(true) in prealabil).
error($message), va afisa un mesaj de eroare tot timpul (indiferent de valoarea setata pentru $debug).
In cea de-a doua parte, la fel ca in exemplul pe care l-am urmat, am implementat interfata ITrace in doua cazuri, FileTrace ce imi va permite sa logez mesajele de eroare/debug intr-un fisier text si StdoutTrace, ce imi va permite sa afisez aceste mesaje in consola (pagina web).
Obligatoriu, cele doua noi obiecte vor trebui sa implementeze metodele prezentate in interfata ITrace.
FileTrace, in exemplul urmat de catre mine, trebuia sa arunce o exceptie in constructor. Am decis ca in cazul in care fisierul in care vrem sa logam mesajele nu exista sau nu se poate scrie in el, sa arunc exceptia, insa daca totul este in regula, sa creez handlerul ( :) ):
$this->handler = fopen($file, "a+").
Am decis sa deschid fisierul in mod a+ (Read/write; place the file pointer at the end of the file), cu toate ca probabil a era destul (nu intentionez sa si citesc fisierul). Detalii despre fopen, aici.
In plus fata de exemplul urmat, am scris si un destructor, dar acesta nu era necesar (PHP va inchide automat dupa terminarea executarea codului handlerul?), iar conditia:
if($this->handler===true)
nu cred ca isi are rostul (poate se intampla altceva in $this->handler = fopen($file, "a+") ?).
Intr-un post ulterior, am sa inlocuiesc aceste erori (?) din FileTrace, inclusiv comentariile (deprecated) cum ca metodele debug($message) sau error($message) ar arunca o exceptie.
Am decis, ca in PHP sa inlocuiesc atributul:
private java.io.PrintWriter pw
cu $handler, si am constatat ca in PHP pentru a scrie ceva intr-un fisier este mai intuitiv si mai usor de retinut.
Comparatie:
pw = new java.io.PrintWriter( new java.io.FileWriter( "c:\trace.log" ) ); (nu o sa retint foarte usor linia asta)
cu
$this->handler = fopen($file, "a+");
din constructori, sau cu modalitatea de scriere (de exemplu):
pw.println( "DEBUG: " + message );
pw.flush(); (??)
cu
fwrite($this->handler, date("d-m-Y H:i:s") . " DEBUG >>>> " . $message . "\n");
Dar, asta este deja o alta discutie.
Pentru o mai mare flexibilitate, am decis ca numele fisierului in care sa logam mesajele sa fie pasat in constructor (parca as vrea sa mearga si pe linux si pe windows in aceiasi maniera, nu folosind un nume de fisier predefinit: c:\trace.log).
Acest lucru, aduce si modificari directe in factory.
In schimb, in StdoutTrace lucrurile sunt destul de clare, exact ca in exemplu, cu mentiunea ca numele obiectului prezentat in exemplu (SystemTrace), l-am inlocuit cu StdoutTrace.

Voi continua cu explicatii pentru TraceFactory.
Exact ca in exemplul urmat, am declarat metoda ca fiind statica, adaugand in plus parametrul $file, reprezentand numele fisierului in care voi loga mesajele.
Folosesc un bloc try/catch pentru a intercepta exceptia aruncata de constructorul din FileTrace.
Poate ca sensul exemplului s-a schimbat in momentul in care am decis sa pasez numele fisierului in care se face logarea.
Ca o mica traducere, incerc (try) sa returnez o noua instanta a obiectului FileTrace, daca nu reusesc acest lucru(catch), initializez StdoutTrace care va merge sigur avand astfel posibilitatea de a afisa mesajele de eroare/debug.

In partea urmatoare, o sa incerc si o implementare a acestui exemplu, cu toate ca acum este destul de intuitiva.


VIII. O Implementare

Am scris fisierul runTrace.php:

<?php
include_once('TraceFactory.php');

$filename = 'logger.log';

touch($filename);

$t = &TraceFactory::getTrace($filename);
$t->error("O eroare de test!");
$t->setDebug(true);
$t->debug("Debug is true");

for($i=1;$i<10;$i++){
$t->debug("Tha lup :: " . $i);
}

unset($t);

$t = &TraceFactory::getTrace();
$t->error("Ceva de test!");
$t->setDebug(true);
$t->debug("True Debug");

for($i=1;$i<10;$i++){
$t->debug("Za I :: " . $i);
}

$t->error("FOO IS BAR MUST DIE !!!");


Dupa executie, in consola rezultatul a fost:

$ php runTrace.php

15-01-2005 14:42:24 ERROR >>>>0
15-01-2005 14:42:24 ERROR >>>>Ceva de test!
15-01-2005 14:42:24 DEBUG >>>>True Debug
15-01-2005 14:42:24 DEBUG >>>>Za I :: 1
15-01-2005 14:42:24 DEBUG >>>>Za I :: 2
15-01-2005 14:42:24 DEBUG >>>>Za I :: 3
15-01-2005 14:42:24 DEBUG >>>>Za I :: 4
15-01-2005 14:42:24 DEBUG >>>>Za I :: 5
15-01-2005 14:42:24 DEBUG >>>>Za I :: 6
15-01-2005 14:42:24 DEBUG >>>>Za I :: 7
15-01-2005 14:42:24 DEBUG >>>>Za I :: 8
15-01-2005 14:42:24 DEBUG >>>>Za I :: 9
15-01-2005 14:42:24 ERROR >>>>FOO IS BAR MUST DIE !!!

iar in fisierul logger.log am :

15-01-2005 14:42:24 ERROR >>>> O eroare de test!
15-01-2005 14:42:24 DEBUG >>>> Debug is true
15-01-2005 14:42:24 DEBUG >>>> Tha lup :: 1
15-01-2005 14:42:24 DEBUG >>>> Tha lup :: 2
15-01-2005 14:42:24 DEBUG >>>> Tha lup :: 3
15-01-2005 14:42:24 DEBUG >>>> Tha lup :: 4
15-01-2005 14:42:24 DEBUG >>>> Tha lup :: 5
15-01-2005 14:42:24 DEBUG >>>> Tha lup :: 6

15-01-2005 14:42:24 DEBUG >>>> Tha lup :: 7
15-01-2005 14:42:24 DEBUG >>>> Tha lup :: 8
15-01-2005 14:42:24 DEBUG >>>> Tha lup :: 9

daca, comentez linia:
touch($filename);
si sterg fisierul logger.log, rezultatul va fi (normal, doar in consola, caci constructorul din FileTrace va arunca exceptia intrucat fisierul nu va exista):

$ php runTrace.php

15-01-2005 14:48:59 ERROR >>>>0
15-01-2005 14:48:59 ERROR >>>>O eroare de test!
15-01-2005 14:48:59 DEBUG >>>>Debug is true
15-01-2005 14:48:59 DEBUG >>>>Tha lup :: 1
15-01-2005 14:48:59 DEBUG >>>>Tha lup :: 2
15-01-2005 14:48:59 DEBUG >>>>Tha lup :: 3
15-01-2005 14:48:59 DEBUG >>>>Tha lup :: 4
15-01-2005 14:48:59 DEBUG >>>>Tha lup :: 5
15-01-2005 14:48:59 DEBUG >>>>Tha lup :: 6
15-01-2005 14:48:59 DEBUG >>>>Tha lup :: 7
15-01-2005 14:48:59 DEBUG >>>>Tha lup :: 8
15-01-2005 14:48:59 DEBUG >>>>Tha lup :: 9
15-01-2005 14:48:59 ERROR >>>>0
15-01-2005 14:48:59 ERROR >>>>Ceva de test!
15-01-2005 14:48:59 DEBUG >>>>True Debug
15-01-2005 14:48:59 DEBUG >>>>Za I :: 1
15-01-2005 14:48:59 DEBUG >>>>Za I :: 2
15-01-2005 14:48:59 DEBUG >>>>Za I :: 3
15-01-2005 14:48:59 DEBUG >>>>Za I :: 4
15-01-2005 14:48:59 DEBUG >>>>Za I :: 5
15-01-2005 14:48:59 DEBUG >>>>Za I :: 6
15-01-2005 14:48:59 DEBUG >>>>Za I :: 7
15-01-2005 14:48:59 DEBUG >>>>Za I :: 8
15-01-2005 14:48:59 DEBUG >>>>Za I :: 9
15-01-2005 14:48:59 ERROR >>>>FOO IS BAR MUST DIE !!!

Totusi, nu sunt multumit de codul scris, si mai mult, pasand $file ca parametru in factory, si implicit si constructorului din FileTrace m-am indepartat prea mult de exemplul de la care am pornit.
O sa incerc sa rescriu FileTrace si TraceFactory pentru a ajunge la utilitatea si concluziile prezentate in exemplul urmat:
citez:
Further, factory methods prove useful when you're not sure what concrete implementation of a class to instantiate. Instead, you can leave those details to the factory method.
In the above examples your program didn't know whether to create FileTrace or SystemTrace instances. Instead, you can program your objects to simply use Trace and leave the instantiation of the concrete implementation to a factory method.

am incheiat citatul.

IX. Ceva Corecturi

Asa cum ziceam in finalul ultimului capitol, m-am decis sa rescriu FileTrace si TraceFactory, pentru a fi cat mai aproape de modelul urmat.

FileTrace.php

<?php
include_once('ITrace.php');
/**
* It logs the trace to a file
*
* @todo: implement a custom Exception class
* @package test.factory.trace
* @version 0.2
* @access public
*/
class FileTrace implements Trace {

/**
* It`s the file handler
* @var handler
* @access private
*/
private $handler;

/**
* debug
* @var bool
* @access private
*/
private $debug;

/**
* The construnctor
* it opens the file handler
*
* @param string the file name
* @return void
* @access public
*/
public function __construct() {
$file = 'logger.log';
if( (!is_file($file)) OR (!is_writable($file)) ) {
throw new Exception(
"The file " . $file . " is not writable,\nOr it dosent exist in: " . __METHOD__ . " at line: " . __LINE__ . "!"
);
}
$this->handler = fopen($file, "a");
}

/**
* Sets the debug
* @param bool debug
* @return void
* @access public
*/
public function setDebug($debug) {
$this->debug = $debug;
}

/**
* It writes a debug message
* only if debug is setted to true
* @param string message
* @return void
* @access public
*/
public function debug($message) {
if($this->debug) {
// only print if debug is true
fwrite($this->handler, date("d-m-Y H:i:s") . " DEBUG >>>> " . $message . "\n");
}
}

/**
* It writes an error message
*
* @param string message
* @return void
* @access public
*/
public function error($message) {
// always print out errors
fwrite($this->handler, date("d-m-Y H:i:s") . " ERROR >>>> " . $message . "\n");
}

/**
* Is the Destructor
* just close the handle
* @access public
* return void
*/
public function __destruct() {
fclose($this->handler);
}
}


TraceFactory.php

<?php
/**
* Is the factory
*
* @version 0.2
* @access public
* @package test.factory.trace
*/

class TraceFactory {
/**
* It trys to get a trace
*
* @access public
* @return mixed Trace Interface implementation
*/
public static function &getTrace() {
try {
include_once('FileTrace.php');
return new FileTrace();
} catch (Exception $ex) {
include_once('StdoutTrace.php');
$t = new StdoutTrace();
$t->error("Could not instantiate FileTrace:\n" . $ex->getMessage());
return $t;
}
}
}


X. Alte explicatii

Am incercat sa ma apropii cat mai mult de exemplul pe care l-am urmat.
Mai bine, constructorul din FileTrace, ar putea fi rescris asa:

function __construct() {
$file = 'logger.log';
$this->handler = @fopen($file, "a");
if($this->handler===false){
throw new Exception("Cannot create the file hanled!");
}
}

insa, pe windows ar fi mai greu de testat, nu ar trebui sa existe ingrijorari legate de faptul ca nu as putea crea fisierul logger.log. Pe linux, tinand cont de permisiunile care trebuiesc setate pentru directorul in care lucrez ar fi totusi posibil.

XI. O alta implementare

Am rescris si mini testul runTrace.php astfel:

<?php
include_once('TraceFactory.php');
$t = &TraceFactory::getTrace();
$t->error("O eroare de test!");
$t->setDebug(true);
$t->debug("Debug is true");
for($i=1;$i<10;$i++){
$t->debug("Tha lup :: " . $i);
}
unset($t);
$t = &TraceFactory::getTrace();
$t->error("Ceva de test!");
$t->setDebug(true);
$t->debug("True Debug");

for($i=1;$i<10;$i++){
$t->debug("Za I :: " . $i);
}
$t->error("FOO IS BAR MUST DIE !!!");


Teste? Rezultatele au fost previzibile. FileTrace nu a aruncat nici o exceptie cu constructorul din capitolul X deci totul a fost logat in fisierul logger.log.
Pot fi linistit acum, imi pot scrie scripturile mai departe, folosind si o metoda de logare a evenimentelor produse pe parcursul lor, fara sa ma intereseze daca se produce intr-un fisier sau pe consola, este treaba factory-ului sa decida lucrul asta.


comentarii si intrebari: oancea at gmail dot com
Sus


Trimis de : aurelianData intrarii : N/ANivel :


[ Profil autor ]
aurelian aurelian - http://amansio.blogspot.com

[ Alte articole ale acestui autor ]
PHP 5, SQLite in Factory?

Gazduire

Tutoriale

Discuta in forum

Parteneri

Copyright © 2001-2014 PHP Romania Add PHPRomania to Google Add PHPRomania to Del.icio.us Add PHPRomania to Stumbleupon Add PHPRomania to Digg Add PHPRomania to Blink Web Hosting | Gazduire web | Campanii SMS | Gazduire Claus Web | Inregistrare Domenii | Anunturi | Jocuri cu bile
Firma de Programare SEOMAXIM va oferta servicii complete de SEO, Programare SEO, Programare Web si Programare Site-uri
Powered by Simplis