venerdì, agosto 30, 2013

PHP - Gestione delle eccezioni


Questo post fa parte di una serie preparata qualche anno fa per delle lezioni su PHP.

Introduzione

Non sempre possiamo dare per scontato che le cose funzionino come dovrebbero.
Supponiamo di dover leggere un file e elaborarlo in qualche modo.
L'istruzione di base è

$lines=file('foo.txt');

Siamo sicuri che il file esista? E che sia leggibile?

Se il file non esistesse, otterremmo

Warning
: file(foo.txt) [function.file]: failed to open stream: No such file or directory in... 

Se il file non fosse leggibile, otterremmo

Warning: file(foo.txt) [function.file]: failed to open stream: Permission denied in...

L'approccio del pessimista

L'approccio del pessimista si basa sullo scrivere del codice di test prima di effettuare l'operazione:

$filename='foo.txt';
if (file_exists($filename) && is_readable($filename))
{
  $lines=file('foo.txt');
}
else
{
  // some code here...
}

I problemi di questo approccio sono:
  1. le cose potrebbero andare male anche per altri motivi oltre a quelli presi in considerazione nel test (il file esiste ed è leggibile, ma ci sono blocchi danneggiati nel disco...);
  2. vengono fatte tre cose anziché una;
  3. nella frazione di secondo tra l'esecuzione dei test e l'esecuzione delle operazioni lo stato potrebbe cambiare (un altro processo cambia i permessi sui file);
  4. in alcuni casi (ad esempio la connessione ad un database con determinate credenziali) non c'è modo di verificare prima se le credenziali sono corrette.
Un vero pessimista quindi non potrebbe usarlo... :-)

L'approccio dell'ottimista

Un ottimista potrebbe dare per scontato che le cose vadano bene, ma tenere in considerazione l'ipotesi (da lui considerata remota) che invece vadano storte. Scriverà quindi un codice di questo genere, mettendo il silenziatore (simbolo @) all'istruzione che potrebbe fallire e controllandone l'esito a posteriori:

$lines=@file('foo.txt');
if ($lines)
{
  print_r($lines);
}
else
{
  // some code here...
}

Anche questo approccio ha dei problemi (vedi al riguardo Five reasons why the shut-op operator (@) should be avoided):
  1. vengono nascosti messaggi di errore insospettabili, rendendo più difficile il debug;
  2. rende l'esecuzione più lenta, perché tutto il meccanismo delle impostazioni (file php.ini) è invocato per cambiare il valore della variabile error_reporting) e il codice non viene ottimizzato (TODO: benchmark?)

L'approccio try... catch


L'approccio serio è di includere il codice che potrebbe fallire in un blocco try e gestire separatamente il possibile errore:

try
{
  $filename='foo.txt';
  if (!$lines=file($filename))
  {
    throw new Exception(sprintf('Could not read file "%s"', $filename));
  };
  print_r($lines);
}
catch (Exception $e)
{
  // do something here...
}


Se si vogliono evitare gli spiacevoli messaggi di warning, si dovrà lavorare sul file di configurazione php.ini (in produzione non li si vuole, nell'ambiente di sviluppo sì). In ambiente di produzione potrà essere utile impostare error_log a true, in modo da avere i messaggi di errore scritti nel log del server web.

Cosa fare nella gestione dell'eccezione dipende dai singoli casi, ma è bene sapere che un oggetto di tipo Exception mette a disposizione delle funzioni membro per accedere a informazioni utili per il debug:

catch (Exception $e)
{
  echo "Something went wrong\n";
  echo sprintf("message: %s\n", $e->getMessage());
  echo sprintf("file: %s\n", $e->getFile());
  echo sprintf("line: %s\n", $e->getLine());
}

con un risultato simile al seguente:

Something went wrong
message: Could not read file "foo.txt"
file:    /var/www/...mycode.php
line:    61

Subclassing delle eccezioni e catene di eccezioni

Può essere utile definire delle eccezioni personalizzate in modo da gestire in maniera specifica i vari problemi che si possono presentare. Inoltre, esiste un meccanismo a catena di blocchi try... catch che consente di recuperare informazioni su cosa è andato storto. Si veda questo esempio completo:

class FileNotReadableException extends Exception
{
}

function readFooFile($filename)
{
  try
  {
    if (!$lines=file($filename))
    {
      throw new FileNotReadableException(sprintf('Could not read file "%s"', $filename)); 
    };
    return $lines;
  }
  catch (Exception $e)
  {
    // there was another kind of error...
    throw $e;
  }
}


try
{
  print_r(readFooFile('foo.txt'));
}
catch (Exception $e)
{
  if ($e instanceof FileNotReadableException)
  {
    echo "I couldn't read the file\n";
    echo sprintf("message: %s\n", $e->getMessage());
    echo sprintf("file:    %s\n", $e->getFile());
    echo sprintf("line:    %s\n", $e->getLine());
    echo "trace:\n";
    foreach($e->getTrace() as $number=>$error)
    {
      echo sprintf("  error %d:\n", $number);
      foreach($error as $key=>$value)
      {
        echo sprintf("    %s: %s\n", $key, $value);
      }
    }
  }
  else
  {
    echo "Something went wrong for an unknown reason...\n";
  }
}

in cui:
  1. viene definita una classe personalizzata FileNotReadableException
  2. viene definita una funzione che lancia un'eccezione presa in carico nel codice principale
  3. viene controllato il tipo di eccezione lanciata (con l'operatore instance_of)

Il risultato potrebbe essere simile al seguente:

I couldn't read the file
message: Could not read file "foo.txt"
file: /var/www/corsophp/loris/lezioni/lezione_eccezioni.php
line: 62
trace:
  error 0:
    file: /var/www/corsophp/loris/lezioni/lezione_eccezioni.php
    line: 76
    function: readFooFile
    args: Array

In alternativa, è possibile impostare una serie di catch in cui si specificano i tipi di eccezione:

...
catch (FileNotReadableException $e)
{
  echo "I couldn't read the file\n";
  echo sprintf("message: %s\n", $e->getMessage());
  echo sprintf("file:    %s\n", $e->getFile());
  echo sprintf("line:    %s\n", $e->getLine());
  echo "trace:\n";
  foreach($e->getTrace() as $number=>$error)
  {
    echo sprintf("  error %d:\n", $number);
    foreach($error as $key=>$value)
    {
      echo sprintf("    %s: %s\n", $key, $value);
    }
  }
}
catch (Exception $e)
{
  echo "Something went wrong for an unknown reason...\n";
}

Il meccanismo è delle catene di eccezioni è alla base degli strumenti di debug di Symfony:


Classi predefinite di eccezioni

La Standard PHP Library mette a disposizione alcuni tipi di classi derivare di eccezioni, che potrebbero essere utilmente utilizzate.
Nell'esempio qui sopra, avremmo potuto scrivere:

class FileNotReadableException extends RunTimeException
{
}

PHP - Passaggio di parametri


Questo post fa parte di una serie preparata qualche anno fa per delle lezioni su PHP.

Passaggio di parametri

Quando si richiama una funzione è possibile passarle dei parametri. Gli esempi che seguono possono essere applicati anche, ovviamente, alle funzioni membro delle classi.
Tutti gli esempi vengono presentati con il relativo test Lime, in modo da abituarsi all'idea dello Unit Testing, di cui abbiamo parlato.

Passaggio per valore

function foobar($a)
{
  $a++;
  return $a;
}

$t=new lime_test(1, new lime_output_color());
$t->is(foobar(5), 6, 'foobar() returns the value incremented by one');

Impostazione di un valore di default


function foobar($a=4)
{
  $a++;
  return $a;
}

$t=new lime_test(2, new lime_output_color());

$t->is(foobar(5), 6, 'foobar() returns the value incremented by one');
$t->is(foobar(), 5, 'foobar() takes 4 as default, and returns 5');

Controllo dei parametri


function foobar($a)
{
  if (!is_integer($a))
  {
    throw new Exception('Function foobar accepts only integers as parameter');
  }
  $a++;
  return $a;
}

$t=new lime_test(3, new lime_output_color());

$t->is(foobar(5), 6, 'foobar() returns the value incremented by one');
try
{
  $n=foobar('abc');
  $t->fail('foobar() does not throw an exception with a string parameter');
}
catch(Exception $e)
{
  $t->pass('foobar() throws an exception with a string parameter');
}

try
{
  $n=foobar(1.2);
  $t->fail('foobar() does not throw an exception with a float parameter');
}
catch(Exception $e)
{
  $t->pass('foobar() throws an exception with a float parameter');
}

Oggetti come parametri


Il controllo del tipo è automatico per gli oggetti, se si indica esplicitamente a che classe devono appartenere:

class BazBar
{
  private $v;
  public function __construct($v)
  {
    $this->v=$v;
  }
  public function getV()
  {
    return $this->v;
  }
  
  public function incV()
  {
    $this->v++;
    return $this;
  }
}

class ExtraBazBar extends BazBar
{
}

function foobar(BazBar $a)
{
  $a->incV();
  return $a->getV();
}

$t=new lime_test(2, new lime_output_color());

$t->is(foobar(new BazBar(5)), 6, 'foobar() accepts a BazBar object');

$t->is(foobar(new ExtraBazBar(5)), 6, 'foobar() accepts an ExtraBazBar object');

Array di parametri


function foobar($parameters=array())
{
  $v=$parameters['value']+$parameters['inc'];
  return $v;
}

$t=new lime_test(1, new lime_output_color());

$t->is(foobar(array('value'=>5, 'inc'=>1)), 6, 'foobar() accepts an array of parameters');

Passaggio per riferimento


function foobar(&$v)
{
  $v++;
  return $v;
}

$t=new lime_test(1, new lime_output_color());

$k=5;
foobar($k);
$t->is($k, 6, 'foobar() changes the value of the variable passed as parameter');

Altre cose utili


In alcuni casi potrebbe essere utile fare ricorso alle funzioni func_num_args(), func_get_args(), ecc. Consultare le relative pagine del manuale.

PHP - Unit testing


Questo post fa parte di una serie preparata qualche anno fa per delle lezioni su PHP.
Adesso consiglierei di usare PHPUnit anziché Lime.

Unit testing

La pratica della predisposizione dei test unitari (unit testing) permette di semplificare le modifiche, semplificare l'integrazione e supportare la documentazione. Buoni motivi per adottarla...

Lime

Lime fa parte del framework Symfony, ma può essere utilizzato anche in maniera isolata. Documentazione al riguardo è rintracciabile nelle pagine del progetto (la guida rimane valida per quanto riguarda Lime, anche se la versione Symfony 1.2 è deprecata). Il codice di Lime è disponibile nel deposito SVN di Symfony.

Uso di Lime

Un semplice esempio di partenza di unit test con Lime è il seguente:

<pre>
<?php
require('../../lib/lime.php');
// impostare il percorso a seconda di dov'è il file

ini_set('error_reporting', E_ALL);

function __autoload($className)
{
    $filename=$className.'.class.php';
    include($filename);
}

$t=new lime_test(4, new lime_output_color());

$calculator = new Calculator();

$t->cmp_ok($calculator->getOperand(0), '===', false, '->getOperand() returns false for an unitialized value');

$t->isa_ok($calculator->setOperand(0,10), 'Calculator', '->setOperand() returns a Calculator object');

$t->is($calculator->getOperand(0), 10, '->getOperand() returns the correct value for an initialized value');

try
{
    $calculator->setOperator('_');
    $t->fail('->setOperator() allows a non valid operator');
}
catch (Exception $e)
{
    $t->pass('->setOperator() throws an exception with a non valid operator');
}


L'output dovrebbe assomigliare al seguente:
1..4
ok 1 - ->getOperand() returns false for an unitialized value
ok 2 - ->setOperand() returns a Calculator object
ok 3 - ->getOperand() returns the correct value for an initialized value
ok 4 - ->setOperator() throws an exception with a non valid operator
# Looks like everything went fine. 

Esercizio

Modificare la classe Calculator aggiungendo nuove funzioni membro e predisporre i relativi test.

PHP - Basi di HTTP


Questo post fa parte di una serie preparata qualche anno fa per delle lezioni su PHP.

Basi di HTTP


È molto importante conoscere alcune cose fondamentali di ciò che avviene quando un browser e un server web comunicano tra loro.
Un esercizio interessante è di usare programmi come curl, che consentono di registrare la comunicazione in un file di testo da analizzare con calma.

Il metodo GET

Ad esempio, quando il browser invia una richiesa a google.com con una richiesta simile alla seguente

GET / HTTP/1.1
User-Agent: curl/7.19.5 (i486-pc-linux-gnu) libcurl/7.19.5 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.15
Host: google.com
Accept: */*

viene rediretto a www.google.com:

HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/
Content-Type: text/html; charset=UTF-8
Date: Tue, 02 Mar 2010 20:15:36 GMT
Expires: Thu, 01 Apr 2010 20:15:36 GMT
Cache-Control: public, max-age=2592000
Server: gws
Content-Length: 219
X-XSS-Protection: 0

<HTML><HEAD><meta http-equiv="content-type" content="text/html;c
harset=utf-8"> <TITLE>301 Moved</TITLE></HEAD><BODY> <H1>301 Mov
ed</H1> The document has moved <A HREF="http://www.google.com/">
here</A>.
</BODY></HTML>

A questo punto fa la richiesta a www.google.com:

GET / HTTP/1.1
User-Agent: curl/7.19.5 (i486-pc-linux-gnu) libcurl/7.19.5 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.15
Host: www.google.com
Accept: */*

e riceve un ulteriore reindirezzamento a www.google.it, con l'invito, questa volta, a memorizzare dei cookies:

HTTP/1.1 302 Found
Location: http://www.google.it/
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Set-Cookie: PREF=ID=bd21c8242c754edb:TM=1267563471:LM=1267563471S=NwB1nU_SnFL-QObq; expires=Thu, 01-Mar-2012 20:57:51 GMT; path=/; domain=.google.it
Set-Cookie: NID=32=R74iZCyObDJF0HSpezx6-94w2O9BpeXSR6mD1uOFT4DuGoMY0J6hhQ-1qEXbs04rWbCSkrrch18q5ATv5jnakX-dDPKbCI2U6rpsG5MR05j0a-NmMDasXR9JdK_pRYK3; expires=Wed, 01-Sep-2010 20:16:35 GMT; path=/; domain=.google.com; HttpOnly

Date: Tue, 02 Mar 2010 20:16:35 GMT
Server: gws
Content-Length: 218
X-XSS-Protection: 0

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> <TITLE>302 Moved</TITLE></HEAD><BODY> <H1>302 Moved</H1> The document has moved <A HREF="http://www.google.it/">here</A>.
</BODY></HTML>

La richiesta delle pagine successive avverrà quindi inserendo nella richiesta i cookies:

GET / HTTP/1.1
User-Agent: curl/7.19.5 (i486-pc-linux-gnu) libcurl/7.19.5 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.15
Host: www.google.it
Accept: */*
Cookie: PREF=ID=bd21c8242c754edb:TM=1267563471:LM=1267563471S=NwB1nU_SnFL-QObq
La risposta spesso arriva al browser "spezzettata" (chunked), in modo che non sia necessario informarlo in anticipo della dimensione del file in arrivo (Content-Length)

HTTP/1.1 200 OK
Date: Tue, 02 Mar 2010 20:16:46 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
...Transfer-Encoding: chunked

1000
<!doctype html><html><head><meta http-equiv="content-type" conte
nt="text/html; charset=ISO-8859-1"><title>Google</title><script>
...
ada
ubmit value="Mi sento fortunato" class=lsb></td><td nowrap width
...
rt=(f=(new Date).getTime());.})();.</script>
0

Ogni singolo chunk viene preceduto dalla dimensione in byte (espressa in esadecimale) dello stesso. La fine della trasmissione è contrassegnata dal valore 0.
(Di questo spezzettamente si occupa direttamente il server web, come sviluppatori PHP non dobbiamo preoccuparcene...)

Il metodo POST

Cosa succede invece in caso di POST? Quando compiliamo il modulo di ricerca di www.php.net inviamo dei dati in questo modo:

POST /search.php HTTP/1.1
User-Agent: curl/7.19.5 (i486-pc-linux-gnu) libcurl/7.19.5 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.15
Host: www.php.net
Accept: */*
Content-Length: 29
Content-Type: application/x-www-form-urlencoded

pattern=functions&show=manual

La risposta che riceviamo potrebbe essere una pagina web, ma è buona pratica ridirigere il browser ad un'altra pagina da richiedere con il metodo GET (ed è quello che succede nel caso concreto).

Sulla differenza tra metodo GET e POST è utile la lettura del documento Methods GET and POST in HTML forms - what's the difference? di Jukka "Yucca" Korpela.

Refresh della pagina

Capita spesso di trovare delle pagine web che fanno scattare il refresh della pagina (spesso, ma non solo, per far scaricare un file). Questo di solito viene ottenuto indicando, all'interno dell'elemento META un contenuto simile al seguente:

<html>
<head>
<meta http-equiv="Refresh" content="0;URL=http://www.example.com/" />
</head>
<body>
<script type="text/javascript" language="javascript">
<!--
location.replace("http://www.example.com/")
-->
</script>
</body>
</html>

Nella risposta del server web compare comunque l'intestazione HTTP appropriata, che viene riportata nella pagina web per sicurezza.

Expires: Sun, 28 Mar 2010 09:19:06 GMT
Date: Sun, 21 Mar 2010 09:19:06 GMT
Refresh: 0;URL=http://www.example.com/
...

Nota: il numero prima dell'URL indica dopo quanti secondi il browser è invitato a fare il refresh della pagina.

Approfondimenti

Oltre ai metodi GET e POST, il protocollo HTTP prevede i metodi HEAD, PUT e DELETE (vedi RFC 2616). I browser non supportano direttamente i metodi PUT e DELETE (se si specificano questi come metodi vengono effettivamente fatte richieste con GET), ma è possibile la loro implementazione tramite Javascript.

Le applicazioni che usano in maniera coerente i metodi GET, POST, PUT e DELETE vengono denominate RESTful. Un'ottima introduzione all'argomento è data in RESTful Web services: The basics di Alex Rodriguez.

HEAD

Con il metodo HEAD vengono chieste solo le metainformazioni su una risorsa, non la risorsa stessa.

PUT

Con il metodo PUT viene richiesta l'esecuzione di un'operazione di inserimento o aggiornamento, invocando un URI in cui la risorsa è chiaramente identificata.
Ad esempio,

PUT /picture/123

è diverso rispetto a 

POST /picture/new

in quanto nel primo caso l'URI riflette la conoscenza, da parte dello user-agent, di come verrà identificata la risorsa, mentre nel secondo si affida al server la decisione su cosa fare dei dati ricevuti.

DELETE

Con il metodo DELETE si chiede l'eliminazione di una risorsa.

Esercizio

Predisporre una pagina web con una form HTML per effettuare la ricerca di una funzione nel sito www.php.net.
Esercitarsi nella predisposizione di form di base e verificare con phpinfo() quali sono i dati ricevuti con il metodo GET e con il metodo POST.

PHP - OOP


Questo post fa parte di una serie preparata qualche anno fa per delle lezioni su PHP.

OOP - Esempio di base

<?php
class WebPage
{
  public function setText($text)
  {
    $this->text=$text;
  }
  public function showPage()
  {
    echo $this->text;
  }
}
$mypage=new WebPage();
$mypage->setText('hello');
$mypage->showpage();

Notare che:
  • manca (volutamente) la chiusura del codice php
  • la pagina prodotta è mooolto scarna (per una sola riga di testo bisognerebbe aggiungere qualche header HTTP, tra l'altro...)
  • tutto il codice è in un unico file, quando sarebbe bene avere invece il codice della classe in un file separato.

Calcolatrice

Vediamo un esempio un po' più complesso:
<?php
class Calculator
{
    private    $operands;
    private    $operator;

    function __construct()
    {
        $this->operands=array();
    }
    
    public function setOperand($n, $v)
    {
        $this->operands[$n]=$v;
        return $this;
    }
    
    public function getOperands()
    {
        return $this->operands;
    }
    
    public function getOperand($n)
    {
        $operands=$this->getOperands();
        if (array_key_exists($n, $this->getOperands()))
        {
            return $operands[$n];
        }
        else
        {
            return false;
        }
    }
    
    public function setOperator($v)
    {
        if (!in_array($v, array('+', '-', '*', '/')))
        {
            throw new InvalidArgumentException('Value ' . $v . ' is not allowed');
        }
        
        $this->operator=$v;
        return $this;
    }

    public function getOperator()
    {
        return $this->operator;
    }
}

Cose da notare:
  • costruttore con nome predefinito
  • definizione di variabili membro private
  • lancio di eccezione
  • interfaccia fluente

Uso della classe:

<?php
ini_set('error_reporting', E_ALL);

function __autoload($className) 
{
    $filename=$className.'.class.php';
    include($filename);
}

$calculator = new Calculator();
$calculator->setOperand(1, 5);
$calculator->setOperand(2, 10);
print_r($calculator->getOperands());
unset($calculator);

Cose da notare:
  • autocaricamento della classe (si evitano include, include_once, require, require_once e amenità del genere)
  • visualizzazione ricorsiva di un array (a scopo di debug)
  • deallocazione della memoria utilizzata dall'oggetto (unset)

L'interfaccia fluente dei metodi setters ci consente di scrivere anche cose come queste, più leggibili:

$calculator = new Calculator();
$calculator
->setOperand(1, 5)
->setOperand(2, 10)
->setOperator('*');

echo sprintf("Operator: '%s'\n", $calculator->getOperator());


Esercizio

  1. aggiungere una funzione membro setOperands() che riceve un array di valori come parametro
  2. aggiungere una funzione membro getResult() che restituisce il risultato dell'operazione
  3. definire il comportamento in caso di valori non coerenti (cosa deve succedere se tento una divisione per zero? e se chiedo il risultato prima di aver impostato operandi e operatore?)
  4. definire una serie di casi di test

PHP - Gestione delle stringhe


Questo post fa parte di una serie preparata qualche anno fa per delle lezioni su PHP.

Gestione delle stringhe

Alcune cose di base per chi proviene da esperienza con altri linguaggi.
<?php
    $name="Mario";
    $surname="Rossi";
    $file="picture1.jpeg";
    // usare una delle seguenti istruzioni
:

    $output="$name $surname ha visto l'immagine $file"; // possibile
    $output=$name . ' ' . $surname . ' ha visto l\'immagine ' . $file; // meglio
    $output=sprintf('%s %s ha visto l\'immagine %s',
        $name, $surname, $file); // ancora megli
o    $output=__('%name% %surname% saw the image %filename%', array(
        '%name%'=>$name,
        '%surname%'=>$surname,
        '%filename%'=>$file,
        ));  // molto meglio (ma dipende dal framework e dagli strumenti i18n


Da notare:
  • sostituzione dei nomi delle variabili con il loro valore nel caso di stringhe racchiuse da virgolette doppie
  • uso del backslash per l'escape dell'apice singolo
  • uso di sprintf o degli strumenti i18n

Quando si usa sprintf, se la stringa è racchiusa da virgolette doppie si possono usare sequenze speciali per andare a capo, per tabulare, ecc. (es. "foo\nbar\tbaz").

Le funzioni per le stringhe sono troppo numerose per essere citate. Conviene tenere sotto mano la pagina del manuale di PHP prima di mettersi a reinventare la ruota...
Ad esempio, può essere conveniente usare str_replace() per fare un trova e sostituisci...

Esercizio

Leggere un file di testo con la funzione file(), scorrere gli elementi dell'array e fare delle operazioni su tutti gli elementi (ad esempio un trova e sostituisci). Concatenare l'array modificato ottenuto con implode() e presentarlo in output.

Si consideri il caso in cui si vuole far comparire un messaggio di benvenuto ad un utente. Il modello di testo di messaggio potrebbe essere memorizzato in un file welcome_template.txt simile al seguente:

Ciao, %name%,
Benvenuto nel sito web %website%,
Oggi è il %date%.
#Questo è un commento e non va visualizzato.
#Ricordarsi di impostare il nome del sito web nel file di configurazione.

La nostra applicazione dovrà impostare alcune variabili, leggere il file e operare dei trova e sostituisci per rimpiazzare  i segnaposto:

$rows=file('welcome_template.txt');
$name='Mario';
$website='PHP Developers In Action';
$date=// aggiungere il codice per trovare la data
/* aggiungere il codice per il trova e sostituisci... */
echo implode("\n", $rows);

PHP - Introduzione agli array

Questo post fa parte di una serie preparata qualche anno fa per delle lezioni su PHP.

Introduzione agli array

Gli array standard possono essere indicizzati da stringhe o da numeri. Ogni elemento di un array può essere un valore normale, un altro array o un oggetto (istanza di classe).

<?php

    $friends=Array(
        'Arthur',
        'Charlie',
        'Ben',
    );

    $friends[]='Donald';

    print_r($friends);
    sort($friends);
    print_r($friends);
    var_dump($friends);
    echo serialize($friends);

Cose da notare:

  • inizializzazione esplicita (in particolare, si veda la virgola anche dopo l'ultimo elemento, conviene che ci sia per future aggiunte)
  • aggiunta di un elemento all'array
  • funzioni varie per la gestione degli array

È lecito naturalmente anche inizializzare un array o un suo elemento con una chiave esplicitamente fornita, e valorizzarlo con oggetti o altri array:

<?php
    $images=Array(
        'bob' => new FamilyPicture('bob'),
        'cat' => new AnimalPicture('cat'),
    );

    $images['andrew']=Array(
        new FamilyPicture('andrew1'), new FamilyPicture('andrew2'),
        );

Funzioni predefinite

Per imparare a usare correttamente le funzioni relative agli array è conveniente costrursi degli esempi specifici. Ad esempio, con un codice come il seguente...

<pre>
<?php

    function line() {
        print(str_repeat("-=-", 10) . "\n");
    }

    $a1=array("Uno", "Due", "tre", "ch4"=>"quattro", "ch5"=>"cinque", "Sei");
    print "originale\n";
    print_r($a1);

    line();
    print "sort()\n";
    $a2=$a1;
    sort($a2);
    print_r($a2);

    line();
    print "ksort()\n";
    // risultati strani
    $a2=$a1;
    ksort($a2);
    print_r($a2);

    line();
    print "asort()\n";
    $a2=$a1;
    asort($a2);
    print_r($a2);

    function cmp($a, $b)
    {
        // si potrebbe usare strnatcasecmp al posto di questa
    if (strtoupper($a) == strtoupper($b)) {
        return 0;
        }
    return (strtoupper($a) < strtoupper($b)) ? -1 : 1;
    }
    line();
    print "usort(, \"cmp\")\n";
    $a2=$a1;
    usort($a2, "cmp");
    print_r($a2);

    line();
    print "uasort(, \"cmp\")\n";
    $a2=$a1;
    uasort($a2, "cmp");
    print_r($a2);

    line();
    line();
    line();

    $b=array(
        array('name'=>'mario', 'surname'=>'rossi', 'age'=>20),
        array('name'=>'giuseppe', 'surname'=>'verdi', 'age'=>40),
        array('name'=>'stefania', 'surname'=>'bianchi', 'age'=>30),
        array('name'=>'giorgia', 'surname'=>'neri', 'age'=>50),
  );
    
    function comparePersons($item1, $item2)
    {
    global $fieldname;
    return (strcmp($item2[$fieldname], $item2[$fieldname]));
    }
    $fieldname='surname';
    usort($b, 'comparePersons');
    print_r($b);

... otteniamo un output come questo:

originale
Array
(
[0] => Uno
[1] => Due
[2] => tre
[ch4] => quattro
[ch5] => cinque
[3] => Sei
)
-=--=--=--=--=--=--=--=--=--=-
sort()
Array
(
[0] => Due
[1] => Sei
[2] => Uno
[3] => cinque
[4] => quattro
[5] => tre
)
-=--=--=--=--=--=--=--=--=--=-
ksort()
Array
(
[ch4] => quattro
[0] => Uno
[ch5] => cinque
[1] => Due
[2] => tre
[3] => Sei
)
-=--=--=--=--=--=--=--=--=--=-
asort()
Array
(
[1] => Due
[3] => Sei
[0] => Uno
[ch5] => cinque
[ch4] => quattro
[2] => tre
)
-=--=--=--=--=--=--=--=--=--=-
usort(, "cmp")
Array
(
[0] => cinque
[1] => Due
[2] => quattro
[3] => Sei
[4] => tre
[5] => Uno
)
-=--=--=--=--=--=--=--=--=--=-
uasort(, "cmp")
Array
(
[ch5] => cinque
[1] => Due
[ch4] => quattro
[3] => Sei
[2] => tre
[0] => Uno
)
-=--=--=--=--=--=--=--=--=--=-
-=--=--=--=--=--=--=--=--=--=-
-=--=--=--=--=--=--=--=--=--=-
Array
(
[0] => Array
(
[name] => giorgia
[surname] => neri
[age] => 50
)

[1] => Array
(
[name] => stefania
[surname] => bianchi
[age] => 30
)

[2] => Array
(
[name] => giuseppe
[surname] => verdi
[age] => 40
)

[3] => Array
(
[name] => mario
[surname] => rossi
[age] => 20
)

)

Esercizio

A partire da un codice come il seguente

<html>
<head><title>sdf</title></head>
<body>
<pre>
<?php


$list=Array(
    'bob'=>1,
    'ada'=>2,
    'charlie'=>0,
);

sort($list);

print_r($list);

?>
</pre>
</body>
</html>

modificare il codice per ottenere:
a) un array ordinato per chiave anziché per valore;
b) un array ordinato per valore, dove però le chiavi sono preservate.

sabato, luglio 06, 2013

A quick introduction to Yii development


Some days ago, I have been invited to write a review for the book Yii 1.1. Application Development Starter, written by Jacob Mumm and Mark Safronov (Packt Publishing). The book is published in the "instant" book series, and is available only in its digital form. I received it as a complimentary copy from the publisher.


In the book you don't find anything that isn't available online amongst the official documentation on Yii's website or on Wikipedia. The example in the "Quick start" chapter is taken from (and links to) the official documentation, bringing your attention to the main aspects to consider and leaving out some less important details. The "Top 5 features you need to know about" chapter is a bit more interesting, because it gives you some hints about features that are a little hidden, with some usage examples. I found particularly interesting the sections about authentication and role-based access control. Again, everything is available on the web, so if you have time and want to achieve a deep knowledge of the subject this book is not for you: just go through the examples in the official documentation, access the forums, run your own experiments. But if you are short of time, and just want to know about the main benefits Yii has to offer, it might be worth buying and reading the book, because it offers a quick introduction to all the basic points you'd have to deal with when projecting and implementing a web application using Yii.