Dependency Injection in PHP parte 2: Container

La settimana scorsa avevo pubblicato un articolo introduttivo alla dependency injection; questa volta faremo un passo avanti  definendo un Container, ovvero una classe il cui scopo è quello di contenere i nostri oggetti e di permettere di recuperarli in modo pratico.

Un primo container primitivo è un oggetto dotato di due metodi set() e get() i quali permettano l’accesso alle istanze rilevanti per la nostra applicazione:

class Container
{
   protected $_Data = array();
   public function get($item_name)
   {
      if (isset($this->_Data[$item_name]) )
         return $this->_Data[$item_name];
      return null;
   }
   public function set($item_name,$item)
   {
      $this->_Data[$item_name] = $item;
   }

}

Questo può sembrare triviale ma in realtà è tutto ciò di cui abbiamo bisogno! : Grazie al container possiamo dedicare una parte dell’applicazione, che chiameremo bootstrapping, alla creazione delle istanze di oggetti che utilizzeremo in altri punti dell’esecuzione.

$connection = new MysqlConnection($host,$user,$password,$db);
$db = new Database($connection);

$logger = new TxtLogger('myfile.txt');

$view = new View('master');

$container = new Container();
$container->set('database',$db);
$container->set('logger',$logger);
$container->set('master_view',$view);

A questo punto non ci resta che passare l’istanza di container ai business objects che necessitano delle istanze di database, logger e view per poi poterle riutilizzare:

//...
public function businessMethod()
{
   $this->doAction();
   $this->container->get('logger')->log(' Action done :) ');

   $userDao = new UserDao( $this->container->get('database') );
}

Per rendere il codice più leggibile potremmo sovrascrivere il magic method __get() in modo da cercare l’attributo richiesto direttamente nel container:

public function __get($name)
{
   if (null !== $this->container->get($name) )
   return $this->container->get($name);

   throw new BusinessObjectException('Unkown property '.$name);
}
public function anAction()
{
   $this->database->query('INSER INTO table VALUES (bla bla bla)');
}

Per approfondire il design di un container, vi consiglio di leggere queste slides scritte da Fabien Potencier, il creatore di Symfony.

Annunci

Dependency Injection in php: Introduzione

Quando ti trovi di fronte ad un progetto che utilizza di un buon numero di classi e istanze, serve un piano d’attacco. Non puoi semplicemente cominciare a scrivere,  altrimenti è molto probabile che, al momento della verità (Il testing), arrivino i problemi. Lo dico con certezza perchè io stesso, fino a qualche tempo fa, ero uno di quelli che iniziava subito a scrivere codice e che poi si ritrovava con mille tab aperte in eclipse per cercare di fixare il problema.

La dependency injection è l’arte di semplificare la creazione e la configurazione delgi oggetti e recentemente, dopo aver spopolato in ambiente Java, ha cominciato a prendere piede in ambiente php; in questi articoli cercherò di riportare ciò che ho imparato a riguardo.

Pensate di avere un oggetto che dovere utilizzare in diversi punti dell’applicazione e che per essere utilizzato ha bisogno di un minimo di configurazione, ad esempio:

/**
*   Nota : pensiamo che il Device non
*   l'abbiamo scritto noi, ma che stiamo utilizzando
*   classi di terze parti altrimenti avremmo potuto
*   modificare il costruttore del device e
*   scrivere new Device($logger,$request)
*/

$device = new Device();
$logger = new XmlLogger();
$logger->prepare('path/to/file.xml');
$device->setLogger($logger);
$device->setRequest(HttpRequest::create()  );
$response = $device->doYourWork();

Prima di riuscire a fare il suo lavoro, il device ha bisogno di alcune righe di preparazione e questo ogni volta che in un punto diverso dell’esecuzione avete bisogno di usare una istanza di device! Diciamo che Device, in questo caso, ha due dipendenze: XmlLogger e HttpRequest.

Premesso che eviterò di usare stati globali come la peste (per maggiori informazioni leggete qui) e quindi qualsiasi forma di Singleton, scrivere quel codice di configurazione ogni volta che si vuole chiamare doYourWork() potrebbe essere una grossa seccatura, specialmente se Device fosse più complesso.

Una prima soluzione ci è data dalla Dependency injection: scriviamo un Container:

class DeviceContainer
{
public function getDevice()
{
   $this->device = new Device();
   $logger = new XmlLogger();
   $logger->prepare('path/to/file.xml');
   $this->device->setLogger($logger);
   $this->device->setRequest(HttpRequest::create()  );
   return $this->device;

}
}

Ora, ogni volta che avremo bisogno di utilizzare il Device, dobbiamo solamente chiamare il metodo getDevice() della classe container ed otterremo una istanza correttamente configurata :

class DeviceUserA
{
   function work()
   {
       $container = new DeviceContainer();
       $device = $container->getDevice();
       $device->doYourWork();
    }

}

Questa soluzione è ancora poco flessibile perchè potreste necessitare all’interno della vostra applicazione di diversi tipi di device ognuno con una configurazione diversa. Dobbiamo modificare il nostro DeviceContainer:

class DeviceContainer
{
public function getDeviceA()
{
   if (null !== $this->device_a)
      return $this->device_a
   $this->device_a = new DeviceA();
   $logger = new XmlLogger();
   $logger->prepare('path/to/file.xml');
   $this->device_a->setLogger($logger);
   $this->device_a->setRequest(HttpRequest::create()  );
   return $this->device_a;

}
public function getDeviceB()
{
   if (null !== $this->device_b)
      return $this->device_b

   $this->device_b = new DeviceB();

   $dsn = 'mysql:dbname=testdb;host=127.0.0.1';
   $user = 'dbuser';
   $password = 'dbpass';
   $connection = new PDO($dns,$user,$password);
   $logger = new MysqlLogger($connection);

   $this->device_b->setLogger($logger);
   $this->device_b->setRequest(HttpRequest::create()  );
   return $this->device_b;

}

}

Potete notare che ogni metodo getDeviceX() ha due righe di codice che consentono di non reistanziare l’oggetto ogni volta
ma di riutilizzare istanze già create dal container.

Per questo articolo è tutto, nel prossimo vedremo come scrivere un container che funzioni in ogni situazione (o quasi) senza bisogno di modificare la classe container.

Ora torno a lavorare a qoo framework, non vedo l’ora di pubblicarlo ed essere criticato 😉 .

Windows Vista + Eclipse + Git + Github

Di recente mi sono stati proposti due progetti davvero interessanti riguardanti il web2.0 per prepararmi al meglio ho deciso di riorganizzare le mie carissime e fidatissime classi php che utilizzo da tempo in un piccolo framework che mi permetta di prototipizzare alcune operazioni comuni.

Come servizio di project hosting mi sono affidato a Github, che come suggerisce il nome utilizza repository Git, ecco quindi i passi da compiere per avere un sistema funzionante:

  • Create un account su Github.com
  • Create un repository su github.com con il nome del vostro progetto
  • Seguite questa guida per installare Git su sistemi Windows
  • Aprite Eclipse e nel tab Help cliccate su “install new software”, selezionate il repository “all available site” e nel filtro di ricerca scrivete Git, dovreste trovare un pacchetto chiamato EGit ed uno chiamato JGit, io ho installato EGit quindi faccio riferimento a quello
  • Al posto di creare un nuovo progetto su eclipse, andate su File->Import->Git->Projects from Git ed inserite i dati relativi al vostro repository git locale
  • Importate con il wizard per semplicità

A questo punto avete il vostro progetto collegato al repository Git hostato da Github.com, manca solo un piccolo dettaglio che mi ha creato 5 minuti di panico :

Per qualche motivo, Eclipse cerca di default le vostre chiavi RSA per SSH nella cartella {USER}/ssh invece di {USER}/.ssh . In rete ho trovato due modi per risolvere il problema in windows. Il primo consiglia di creare un link alla directory .ssh e chiamarlo ssh in modo che eclipse la trovi (il comando in questione dovrebbe essere mklink \D .ssh ssh). Il secondo metodo che ha funzionato per me, consiste nel modificare una opzione di eclipse, ovvero andate su Window->Preferences->General->Network Connection->SSH2 e mettete il punto davanti alla benedetta directory ssh.

Buon teamwork a tutti 🙂

Script Bash che sorride alla connesione!

Ispirato da un intervento del Linux Date di Ancona ho scritto un piccolo script per testare la connessione ad internet tramite l’utilizzando Ping e cosa farmene (a parte testarci la mia connessione :D) se non condividerlo con voi!!!

if ping -c1 www.google.com >/dev/null; then echo ":)" else echo ":(" fi;

Ho scelto di pingare google per avere la (quasi totale) certezza che il server sia attivo 😀

Naturalmente basta salvarlo in un file come ad esempio test.sh ed eseguirlo nel modo seguente:

fatmatt@debian#: sh test.sh

Vi auguro di non vedere nessuna “:(” 😀

Pillole contro il mal di XSS

Continua la mia serie di mini articoli sull’argomento XSS, che, a quanto pare, incuriosisce moltissimi naviganti della rete 😀 (mi riferisco alle statistiche dei motori di ricerca 😉 ).

xsswarning.jpg

Questo articolo non è esattamente un tutorial riguardante una tecnica di XSS, ma più che altro una raccolta di pillole di saggezza che tutti dovrebbero assimilare.

  • Controllate la presenza di tags HTML e rendeteli inoffensivi (non del tutto!) con la funzione php strip_tags()
  • Se non potete rimuovere a priori i tag html dalle query, rimpiazzateli con il corrispondente riferimento ad entità: la funzione htmlentities() sostituisce i caratteri “<” e “>” con “<” e “>”
  • Evitate di utilizzare le funzioni include() e require() passando come argomento variabili definite dall’utente, come elementi degli array $_POST $_GET $_REQUEST e $_FILE, potreste dar luogo a vulnerabilità critiche: inclusione di files di installazione del vostro CMS oppure files di configurazione di vario genere contenenti username, password ed altre informazioni importanti.

Filtrate approfonditamente le query, controllando anche la codifica ASCII ed HEX

Attenti all’utilizzo delle MAGIC QUOTES: Un tentativo da parte degli sviluppatori PHP di incrementare la sicurezza di PHP tramite il filtraggio automatico delle stringhe, aggiungendo il carattere di escape. Questa opzione non vi mette al riparo dagli attacchi di XSS e SQL Injection, poiché il controllo può in molti casi essere bypassato. Inoltre, il sistema di MAGIC QUOTES comporta alcuni problemi di portabilità del codice, che potrebbe assumere comportamenti diversi in base alla configurazione della vostra installazione di PHP.

Directory sotto controllo con Perl

folder_locked.png
Navigando per CPAN, l’imponente archivio dei moduli Perl, mentre cercavo delle funzioni per smanettare con i permessi sui file, mi sono imbattuto nel modulo File::Monitor, che permette di monitorare file e directory in cerca (o in attesa) di cambiamenti di qualsiasi tipo.
Capirete che questo genere di funzionalità puà rivelarsi molto utile qualora si volesse monitorare lo stato di una directory importante a cui nessuno dovrebbe apportare modifiche, come ad esempio le directory di installazione del vostro sito internet, le directory contenenti informazioni sugli utenti che dovrebbero essere modificate solo dall’amministratore di sistema o dal sistema stesso ecc.. .
Questo modulo offre una classe principale File::Monitor, a cui assegnare diversi elementi da controllare e grazie ad un sistema di gestione degli eventi, ad ogni occorrenza di un determinato tipo di evento, potrete eseguire una specifica serie di istruzioni (contenute in subroutine).
Ecco la lista gerarchizzata dei possibili tipi di evento:

change

created

deleted

metadata

time

mtime

ctime

perms

uid

gid

mode

size

directory

files_created

files_deleted

Ogni volta che File::Monitor rileva un cambiamento in uno dei suoi “bersagli”, incapsula le informazioni a riguardo in un oggetto di tipo Delta, da cui poi possiamo estrarle con dei semplici metodi.

Ecco un semplice esempio di come monitorare una directory in modo superficiale, senza controllare nel dettaglio i cambiamenti apportati ai files, naturalmente.

Questo esempio è molto esemplificato e se voleste scrivere uno script che controlli ogni aspetto dei vostri files, dovreste aggiungere istruzioni per ogni tipo di evento.

use File::Monitor;sub getDate() {

#Funzione che restituisce la data nel formato: gg/mm/aaaa--hh-mm-ss;

 	@time = localtime();

#è necessario aggiungere 1900 perchè localtime contiene il numero di anni passati dal 1900

 	$time[5] = $time[5]+1900;

#è necessario aggiungere 1 al mese perchè localtime conta i mesi a aprtire da 0

 	$time[4] = $time[4]+1;

 	return $time[3]."/".$time[4]."/".$time[5]."--".$time[2].":".$time[1].":".$time[0];

}

sub DIRchanged() {

#Ecco cosa facciamo quando la directory cambia

                open(FH,">>/tmp/dirMonitor.log") || die "ERRORE!!\t Impossibile aprire il log!!\n";

 	print "Data: ".&getDate()."\t";

 	print "ATTENZIONE!! La directory $DIR ha subito delle modifiche!!\n";

 	print FH "Data: ".&getDate()."\t";

 	print FH "ATTENZIONE!! La directory $DIR ha subito delle modifiche!!\n";

 	close(FH);

            }

#Oggetto fondamentale

my $monitor = File::Monitor->new();

#Prendiamo la directory da controllare, che è passata argomento

unless (-d $ARGV[0]) { die("ERRORE:\t La directory da monitorare specificata, non esiste!!\n\n")

$DIR = $ARGV[0];

$monitor->watch( {

        name        => $DIR,

        recurse     => 1,

        files       => 0

    } );

#Il primo scan on rileva cambiamenti ma raccoglie le informazioni sui files

$monitor->scan();

#Per ogni cambiamento, esegue i controlli che vogliamo

foreach my $change ($monitor->scan()) {

if ($change->is_event('changed')) {

&DIRchanged();

}

}

Per prima cosa ho incluso la libreria Monitor, poi ho definito due subroutine:
getDate(): formatta la data corrente in modo approfondito fino ai secondi
DIRchanged(): risponde all’evento changed modificando un file di log ed emettendo il segnale di allarme allo Standard Output

Creo l’oggetto monitor, e poi controllo che esista la directory da controllare passata come argomento da riga di comando, altrimenti termina con un messaggio di errore.

Il metodo watch(), assegna la directory da controllare e setta i flag che ne definiscono il comportamento:
recurse: Se settato a true e il flag base è una directory, scansiona ricorsivamente tutto l’albero directory sottostante alla directory specificata
files: Se settato a true e il flag base è una directory, scansiona tutti i file e directory sottostanti alla directory specificata, ma non ricorsivamente

In questo modo però solamente i nomi dei files e delle directory vengono controllati, per approfondire il controllo dobbiamo lavorare con i Delta Objects.

A questo punto, lanciamo il primo scan, che non rileva cambiamenti dei files in quanto stiamo facendo il primo controllo ed il monitor non ha ancora nessuna informazione a riguardo, ma si tratta della fase di acquisizione delle informazioni su di essi.

Tutti gli scan successivi restituiscono un oggetto di tipo Delta, su cui effettueremo i nostri test.

Perl ed il modulo Term::ReadLine

Vi siete mai chiesti come implementare un prompt dei comandi per le vostre applicazioni? Perl vi fornisce un modulo perfetto allo scopo, che in poche righe di codice (12 nel caso del nostro esempio) vi permette di configurare un terminale con tanto di storico dei comandi inseriti.
Buttiamoci subito nell’ analisi del codice:

##############Config########################
 my $PROMPTSTRING = '%PSH% >>>';
 my $PROMPTNAME = 'term';
 ##############Main########################
 use Term::ReadLine;
 my $term = new Term::ReadLine $PROMPTNAME;
 my $out = $term->OUT || \*STDOUT;
 while (defined($_ = $term->readline($PROMPTSTRING))) {
  my $result = eval($_);
  warn $@ if $@;
  chop($result);
  print $result, "\n" unless $@;
  $term->addhistory($_) if /\s/;
}

Le prime due istruzioni, impostano il messaggio che visualizzeremo nel prompt ed il nome del terminale, mentre la terza include il modulo perl che fornisce tutte le subroutines necessarie (si trova facilmente il deb nei repository Debian), non c’è bisogno che vi ricordi che senza quel modulo non funziona una mazza vero?.

Dopo l’inclusione del modulo inizia la parte interessante:
Creiamo l’oggetto Term::ReadLine ed impostiamo come output, la variabile $out, che conterrà l’output del prompt stesso e quello emesso dallo standard output.

Il ciclo while tiene vivo il terminale, salvando il comando immesso dall’utente nella variabile $_; quindi ad ogni ciclo, viene valutata la stringa $_ come se fosse una istruzione perl e viene eseguita come sottoprogramma (tutto questo grazie alla funzione eval() ).
Infine visualizziamo l’output oppure gli eventuali messaggi di errore per poi aggiungere il nostro comando alla history.

Per maggiori informazioni vi rimando a: