Package e accesso

Ultime due lezioni, numero 10 sui package e 11 sui modificatori d'accesso, del corso Sun introduttivo a javaFX.

Niente da dire a commento aggiuntivo sui package, tutte cose che chi ha usato java conosce già. E quindi passiamo direttamente all'ultima lezione.

Accesso default

Se non specifichiamo nessun accesso intendiamo utilizzare l'accesso di default (come pare ragionevole), noto anche in javaFX come script-only, nel senso che tutto ciò che é definito in questo modo é visibile solo nel file corrente.

Questa modalità d'accesso é evidentemente specifica per javaFX, dato che in java non c'é questa interpretazione di un singolo file come unità a sé stante.

Accesso package

E' in javaFX quello che é per java l'accesso default, creando probabilmente qualche confusione. Anche perché per dichiarare che una variabile ha in javaFX un accesso package devo scrivere qualcosa del genere:

package var x;

Che può confondere le idee rispetto al concetto di package come unità contenente classi (e script).

In ogni caso: una variabile, classe o funzione dichiarata di accesso package é visibile dal codice presente nello stesso package.

Come esempio di uso della visibilità a livello package, creamoci un paio di file, nel primo, one.fx, scriviamo questo codice:

// tutorial/one.fx
package tutorial; // (1)

package var message = "Hello from one.fx!"; // (2)

// (3)
package function printMessage() {
println("{message} (in function printMessage)");
}

1: dichiariamo che il file é nel package tutorial.
2: definiamo una variabile con visibilità limitata al package corrente
3: definiamo una funzione con visibilità limitata al package corrente

Creiamo quindi un altro file, two.fx, che usa quanto definito in one.fx:

// tutorial/two.fx
package tutorial; // (1)

println(one.message); // (2)
one.printMessage(); // (3)

1: dichiariamo che il file é nel package tutorial.
2: usiamo la variabile message definita nell'oggetto one package corrente
3: definiamo la funzione printMessage() definita nell'oggetto one del package corrente

Questo modo di accedere variabili e fuzioni definite in one.fx ci spiega anche come funziona il trucco che permette a javaFX di farci definire codice e funzioni in un file all'esterno di classi: viene evidentemente definito un oggetto implicito che ha lo stesso nome del file.

Quindi per accedere la funzione printMessage() definita nel file one.fx scriviamo semplicemente:

one.printMessage();

Accesso protected

Qui siamo nel terreno conosciuto delle modalità di accesso java: l'accesso protected fa in modo che quanto definito sia accessibile all'interno del package corrente e da tutte le classi derivate da ogni classe del package stesso.

Riscriviamo l'esempio precedente dando, in one.fx, visibilità protected a un membro di classe pubblica:

package tutorial;

public class one {
protected var message = "Hello!";
}

La classe one sarà accedibile da ovunque, essendo pubblica, e message sarà accedibile da tutte le classi derivate da one, essendo protected. Per veder ciò, riscriviamo two.fx in questo modo:

import tutorial.one;

class two extends one {
function printMessage() {
println("Class two says {message}");
}
};

var t = two{};
t.printMessage();

Da notare, oltre a quanto detto per protected, un paio di cosettine: in javaFX la convenzione sui nomi é decisamente più rilassata (almeno in questo tutorial), e le classi hanno nomi che cominciano con lettere minuscole. Inoltre l'oggetto di tipo two viene costruito qui con la sintassi specifica di javaFX, ovvero, e non con quella java (var t = new two();).

Accesso public

Come per il public in java, massima accessibilità garantita per chi ha questo livello di accesso.

Accesso public-read

Altra interessante estensione di javaFX alle modalità di accesso proprie di java. Con public-read diciamo che una variabile é accedibile in lettura a tutti, ma può essere modificata solo nello script corrente.

E' possibile cambiare i privilegi in scrittura a questa variabile abbinando a public-read il modificatore di accesso package o protected.

Accesso public-init

Estende ulteriormente la public-read, rendendo la variabile anche inizializzabile da tutti. Ma, dopo l'inizializzazione, accedibile solo in lettura a livello public. L'accesso in scrittura, dopo la creazione, é gestito come nel caso public-read, ovvero solo nello script corrente per default.

Scrivere classi

Con la lezione numero 9 del corso Sun introduttivo a javaFX, torniamo a vedere cose che, per chi abbia un background java sono piuttosto scontate, si parla infatti, con un po' più di dettaglio di quanto si sia fatto precedentemente, di classi e oggetti in javaFX.

La differenza fondamentale tra java e javaFX, é che é stato rimossa la limitazione sul nome del file che dove corrispondere al nome della classe principale contenuta nel file stesso. E quindi si possono mettere tutte le classi che vogliamo in un unico file. Questo rende probabilmente la struttura di un progetto FX meno lineare di un java classico ma permette una più semplice scrittura di script.

Approfitteremo di questa feature di javaFX per scrivere tutto il codice di cui parliamo in questo post in un unico file fx.

Prima cosa, definiamo la classe Address in questo modo:

class Address {
var street: String;
var city: String;
var state: String;
var zip: String;
}

In pratica un indirizzo viene definito come un aggregato di quattro stringhe: via, città, stato, e cap.

Un poco più interessante la classe Customer, che definisce come suo dato membro un oggetto di tipo Address e tre funzioni che usano i dati della classe:

class Customer {
var firstName: String;
var lastName: String;
var phoneNum: String;
var address: Address;

function printName() {
println("Name: {firstName} {lastName}");
}

function printPhoneNum(){
println("Phone: {phoneNum}");
}

function printAddress(){
println("Street: {address.street}");
println("City: {address.city}");
println("State: {address.state}");
println("Zip: {address.zip}");
}
}

Per costruire un oggetto di tipo Customer, facciamo come si vede qui a seguire. In questo caso l'oggetto é costante (visto il def che lo introduce) ma sarebbe ovviamente potrebbe essere variabile:

def customer = Customer {
firstName: "John";
lastName: "Doe";
phoneNum: "(408) 555-1212"
address: Address {
street: "1 Main Street";
city: "Santa Clara";
state: "CA";
zip: "95050";
}
}

Definito customer di tipo Customer possiamo ora chiamare le funzioni definite per esso in questo modo:

customer.printName();
customer.printPhoneNum();
customer.printAddress();

Gerarchie di classi

E' possibile scrivere gerarchie di classi, dove classi figlie ereditano il comportamento di una classe madre. Se marchiamo una classe come astratta intendiamo che non siano creati oggetti di quel tipo. Vediamo qui un esempio:

abstract class Account {
var accountNum: Integer;
var balance: Number;

function getBalance(): Number {
return balance;
}

function deposit(amount: Number): Void {
balance += amount;
}

function withdraw(amount: Number): Void {
balance -= amount;
}
}

Data questa classe base possiamo definire ora una classe che la estende, aggiungendo dati e funzioni:

class SavingsAccount extends Account {
var minBalance = 100.00;
var penalty = 5.00;

function checkMinBalance() : Void {
if(balance < minBalance){
balance -= penalty;
}
}
}

Ed é possibile anche fare in modo che una classe figlia abbia un comportamento diverso dalle madre, possiamo infatti ridefinire una funzione nella classe figlia usando la parola chiave override:

class CheckingAccount extends Account {
var hasOverDraftProtection: Boolean;

override function withdraw(amount: Number) : Void {
if(balance-amount < 0 and hasOverDraftProtection){
// code to borrow money from an overdraft account would go here
}
else {
balance -= amount; // may result in negative account balance!
}
}
}

Visualizzare un riassunto

Una semplice modifica al template di blogger per far sì che sulla pagina principale del proprio blog venga visualizzata solo la prima parte del post.

Scopo di questo cambiamento é dare a chi accede il blog su blogspot una vista complessiva di quanto é pubblicato, lasciandogli la possibilità di andare sulla pagina che gli interessa per vedere il post nella sua interezza.

Questo dovrebbe rendere la home del blog più pulita e leggibile.

Per far questo, creo una nuova class per il tag span e la uso nel testo del nostro post.

In pratica ogni nostro post sarà strutturato in questo modo:

Qui il riassunto che voglio sia visualizzato:
  • nella home del mio blog,
  • nella pagina specifica del post
<span class="fullpost">
Qui il corpo del post, visibile solo nella sua specifica pagina.
</span>

Il template del mio blog dovrà essere cambiato per riflettere l'utilizzo di questa nuova classe:

Subito prima della fine della sezione head nel mio template, aggiungo questo pezzo di codice:

<!-- riassunto o completo -->
<style>
<b:if cond='data:blog.pageType == "item"'>
span.fullpost {display:inline;}
<b:else/>
span.fullpost {display:none;}
</b:if>
</style>
<!-- riassunto o completo -->

Potrebbe bastare così, ma aggiungiamo sulla home del mio blog anche un link al post completo subito dopo il riassunto, per renderne più semplice l'accesso al lettore.

Vado quindi a cercarmi nel template la fine della sezione dedicata al testo del post pubblicato, che sarà questo tag:

<data:post.body/>

Subito dopo metterò il mio codice, che genererà un link all'URL della pagina specifica per il mio post:

<!-- riassunto o completo -->
<b:if cond='data:blog.pageType != "item"'>
<p><a expr:href='data:post.url'>Leggi il post completo</p>
</b:if>
<!-- riassunto o completo -->

Il risultato di queste modifiche lo si vede sulla pagina principale di questo blog.

Data Binding e Trigger

La lezione numero 8 del corso Sun introduttivo a javaFX é invece piuttosto interessante, si parla qui di due funzionalità utili che sono implementate in modo elegante.

Per prima cosa parliamo di binding, ovvero della possibilità di correlare dati in modo che il cambiamento di uno si rifletta immediatamente nell'aggiornamento dell'altro.

E' ad esempio una funzionalità utilissima per codice progettato secondo i principi del MVC (Model View Controller), dove il Controller deve tenere in sincronia Model e View. La possibilità di stabilire esplicitamente un legame tra diverse variabili rende il codice molto più leggibile e manutenibile.

Vediamo ora esempi piuttosto banali di binding, giusto per prendere confidenza con l'implementazione javaFX del concetto.

Dichiariamo una variabile x e poi creiamo una costante y definita come connessa a x. Il fatto che sia costante ci impedisce di modificarla direttamente, il fatto che sia definita come "bind x" fa sì che sia in pratica un alias di x, ogni cambiamento di x si riflette in una cambiamento di y:

var x = 0;
def y = bind x;
x = 1;
println(y); // 1
x = 47;
println(y); // 47

E' possibile legare dati membro di una classe a variabili indipendenti:

var myStreet = "1 Main Street";
var myCity = "Santa Clara";
var myState = "CA";
var myZip = "95050";

def address = Address {
street: bind myStreet;
city: bind myCity;
state: bind myState;
zip: bind myZip;
};

println("address.street == {address.street}");
myStreet = "100 Maple Street";
println("address.street == {address.street}");

E' possibile creare un legame tra variabili usando una funzione, specificando la parola chiave bound:

var scale = 1.0;

bound function makePoint(xPos : Number, yPos : Number) : Point {
Point {
x: xPos * scale
y: yPos * scale
}
}

class Point {
var x : Number;
var y : Number;
}

var myX = 3.0;
var myY = 3.0;
def pt = bind makePoint(myX, myY);
println(pt.x); // 3

myX = 10.0;
println(pt.x); // 10

scale = 2.0;
println(pt.x); // 20

Nota che si crea un legame tra le variabili locali passate come parametri alla funzione e i dati membro dell'oggetto creato dalla funzione stesso. Una funzione bound é usata in pratica come un costruttore di oggetto che mantiene un legame con i parametri passati.
Nota inoltre che anche quando viene cambiato un valore globale utilizzato dalla funzione bound (nel nostro caso scale) i valori dell'oggetto creato dalla funzione bound vengono ricalcolati.

E' possibile anche usare bind in congiunzione con for per generare una nuova sequenza:

var seq1 = [1..10];
def seq2 = bind for (item in seq1) item*2;
printSeqs();

function printSeqs() {
println("First Sequence:");

for(i in seq1){
println(i);
}
println("Second Sequence:");
for(i in seq2){
println(i);
}
}

Come ci si può aspettare, la seq2 contiene dieci elementi, ognuno del quale é il doppio del corrispondente elemento della prima sequenza.

Trigger

Un trigger é, in pratica, una funzione che viene eseguita quando accade un certo evento su dei dati. In javaFX é possibile scrivere del codice che viene eseguito quando una certa variabile viene modificata usando il costrutto on replace, come esemplificato a seguire:

var password = "foo" on replace oldValue {
println("Old Value: {oldValue}");
println("New Value: {password}");
};

password = "bar";

Il trigger mantiene in oldValue, o come la voglio chiamare, il valore prima del replace, e quindi esegue il codice. Se facciamo correre il codice, vediamo che il trigger é eseguito due volte: quando la variabile su cui é posto viene inizializzata a "foo", e quando il suo valore viene cambiato in "bar".

Operatori ed espressioni

Coppia di lezioni piuttosto noiose, la sei e la sette, all'interno del tutorial sun introduttivo a javaFX.

Gli operatori sono in pratica gli stessi di java/c++, con qualche piccola variazione per far diventar matto il programmatore, vediamoceli rapidamente.

Per l'assegnamento si usa un'uguale (=)

result = num1 + num2;

Gli operatori aritmetici sono +, -, *, /, mod:

result = 10 mod 7; // 10 / 7 = 1 col resto di 3, dunque result sarà 3.

E' possibile combinare gli operatori aritmetici con l'assegnamento, con l'eccezione del modulo:

result += 1; // equivalente a result = result + 1;

Gli operatori unari sono -, --, ++, not (negazione booleana)

var success = false;
println(success); // false
println(not success); // true

Non ci sono sorpese negli operatori di uguaglianza e relazione: ==, !=, >, >=, <, <= Mentre gli operatori condizionali sono and, or Anche l'operatore per il confronto dei tipi é una vecchia conoscenza (per chi conosca java, si intende): instanceof

Espressioni

Un blocco é, a tutti gli effetti, equivalente a una funzione inline: circondato da parentesi graffe, "ritorna" il valore dell'ultima espressione inclusa. Se non c'é alcuna espressione inclusa, il blocco é di tipo Void

var nums = [5, 7, 3, 9];
var total = {
var sum = 0;
for (a in nums) {
sum += a
};
sum;
}

println("Total is {total}."); // 24

Il flusso condizionale di esecuzione é solitamente implementato usando la solita struttura if - else if - else:

if (age < ticketprice =" 0;"> 65) {
ticketPrice = 5;
}
else {
ticketPrice = 10;
}

Ma in javaFX é disponibile anche questa scorciatoia che appare molto concisa ed elegante:

ticketPrice = if (age <> 65) 5 else 10;

Le sequenze, che aevamo visto qui, sono, parlando tecnicamente, delle espressioni di ambito (in inglese: range) di cui é possibile specificare la distanza tra i vari elementi usando la parola chiave step:

var odds = [1..10 step 2];
var down = [10..1 step -1];

I loop su una sequenza sono implementati elegantemente con un for, iterando sugli elementi:

var days = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];

for (day in days) {
println(day);
}

Nota che la variabile day é generata al volo dal compilatore per esistere solo per la durata del ciclo for.

Il ciclo while ha invece una costruzione più tradizionale:

var count = 0;
while (count < count ="=">

Per modificare il flusso di esecuzione all'interno di un ciclo sono disponibili le espressioni continue e break:

for (i in [0..10]) {
if (i > 5) {
break;
}

if (i mod 2 == 0) {
continue;
}

println(i);
}

La gestione delle eccezioni avviene secondo lo schema stabilito dalle espressioni throw (per lanciarla) catch (per gestirla) finally (per specificare cosa fare in ogni caso, dopo aver gestito l'eccezione, o se non c'é stata eccezione)

...
if(somethingWeird) {
throw new Exception("Something weird just happened!");
}

...

try {
foo();
}
catch (e: Exception) {
println("{e.getMessage()} (but we caught it)");
}
finally {
println("We are now in the finally expression...");
}

Virtualizzazione - Iniziative di AMD

Terzo capitolo di Virtualization for Dummies di Bernard Golden e Clark Scheffy che può essere scaricato gratuitamente, nell'edizione speciale Sun AMD, dal sito della sun.

Nei processori Quad-Core AMD Opteron viene implementata una ulteriore ottimizzazione hardware per la gestione della memoria, detta Rapid Virtualiztion Indexing.

Per migliorare il supporto della virtualizzazione nel campo delle connessioni di rete e della gestione della memoria di massa, AMD introduce il concetto di IOMMU: I/O Memory
Management Unit.

Virtualizzazione - La tecnologia AMD

Secondo capitolo di Virtualization for Dummies di Bernard Golden e Clark Scheffy che può essere scaricato gratuitamente, nell'edizione speciale Sun AMD, dal sito della sun.

Quattro risorse di un computer sono cruciali per la virtualizzazione:
  • Processore: La capacità di coordinare l'accesso alla CPU da parte dei diversi sistemi operativi ospitati dalla macchina é una delle sfide principali per la virtualizzazione, anche in considerazione del fatto che l'architettura hardware dei processori x86 non é stata progettata per il supporto di molteplici ospiti.
  • Memoria RAM: Neanche l'uso di questa risorsa é stato progettato per x86 avendo in mente il supporto multi-ospite.
  • Rete: Non si può pensare ai computer contemporanei se non in connessione via Internet
  • Memoria di massa: ogni sistema ospite virtuale deve avere la propria memoria di massa e il software di virtualizzazione deve mantenere isolata la memoria di massa di ogni sistema ospite.
In un ambiente virtualizzato é vitale salvare e gestire correttamente lo stato di ogni sistema ospite. L'hypervisor di virtualizzazione deve essere capace di gestire bene e velocemente gli stati dei vari sistemi ospite.

E' da molto che si adopera il concetto di memoria virtuale. La memoria RAM viene salvata sulla memoria di massa e ripristinata all'occorrenza, simulando una maggior quantità di memoria RAM a disposizione.

La CPU dell'Opteron di AMD é disegnata per permettere ottimizzazioni che velocizzano il passaggio di contesto tra i vari sistemi ospite, inoltre usano meno energia adattandosi a quelli che sono le richieste di carico di lavoro correnti.

Virtualizzazione - concetti base

Nonostante il titolo, mi sto leggendo Virtualization for Dummies di Bernard Golden e Clark Scheffy che può essere scaricato gratuitamente, nell'edizione speciale Sun AMD, dal sito della sun.

Alcuni concetti chiave che rendono la virtualizzazione un tema interessante:
  • normalmente l'hardware é sottoutilizzato, spesso le macchine usano solo il 10-15% della loro capacità di calcolo.
  • il numero di server utilizzati é cresciuto drammaticamente negli ultimi anni. La virtualizzazione permette di sfruttare meglio le macchine esistenti, riducendo la necessità di nuove macchine.
  • una nuova coscienza ecologica spinge a utilizzare meglio l'hardware esistente.
  • i costi per l'amministrazione dei sistemi aumentano.
Sono tre i principali tipi di virtualizzazione del server:
  • virtualizzazione del sistema operativo: la virtualizzazione di un sistema operativo é in esecuzione su un altro sistema operativo. Le applicazioni in esecuzione sul sistema operativo virtualizzato hanno l'illusione di avere l'intera macchina a disposizione.
  • emulazione hardware: il software di virtualizzazione (detto hypervisor) presenta un emulazione hardware della macchina su cui lavora il sistema operativo ospite. Questo ambiente viene detto VMM (Virtual Machine Monitor). In questo modo diversi sistemi operativi possono coesistere sulla stessa macchina.
  • paravirtualizzazione: in pratica l'idea é quella di permettere la coesistenza di diversi sistemi operativi sulla stessa macchina limitandosi ad arbitrare l'accesso alle risorse fisiche da parte dei diversi sistemi che sono quindi in competizione tra loro.
E questo direi che é quanto, per il primo capitolo.

Sequenze

Nella quinta lezione del tutorial sun su javaFX si parla di sequenze.
Una sequenza é una lista ordinata di oggetti. Un concetto simile all'enumerazione.

Al solito, se non si specifica il tipo della sequenza, ci pensa il compilatore ad inferirne uno. Ad esempio, queste due definizioni di sequenze costanti sono equivalenti:

def weekDays = ["Mon","Tue","Wed","Thu","Fri"];
def weekDays2: String[] = ["Mon","Tue","Wed","Thu","Fri"];

Si può usare una sequenza precedentemente definita per crearne un'altra. Queste due sequenze, definite come variabili sono perciò equivalenti - se weekDays é la sequenza definita qui sopra:

var days = [weekDays, ["Sat","Sun"]];
var days2 = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];

Per creare sequenze che formino una serie aritmetica si può usare questa notazione semplificata:

def nums = [1..100];

Si può usare una espressione booleana (ovvero un predicato) per creare una nuova sequenza a partire da una esistente:

def evenNums = nums[n | (n mod 2) == 0];

La sequenza evenNums contiene tutti i valori che erano in nums tali che il resto della loro divisione per due sia uguale a zero, ovvero i numeri pari.

L'accesso agli oggetti contenuti in una sequenza può essere effettuato sulla base dell'indice dell'elemento, a partire dall'indice 0, come da convenzione ereditata dal linguaggio C:

println(days[0]); // "Mon"

L'operatore sizeof ci permette di vedere quanti elementi siano in una sequenza:

println(sizeof evenNums); // 50

Una sequenza può essere definita come variabile (var) o costante (def). Nel primo caso, quindi, può essere modificata. Si noti che, tecnicamente parlando, le sequenze sono immutabili, quello che succede quando variamo una sequenza é che la vecchia viene distrutta e una nuova, variata secondo la nostra richiesta, ci viene ritornata.

Per inserire un nuovo elemento in coda a una sequenza usiamo il costrutto insert into:

var days = ["Mon"];
insert "Tue" into days;
insert "Fri" into days;
insert "Sat" into days;
insert "Sun" into days;

Possiamo inserire un nuovo elemento prima o dopo di un determinato indice con i costrutti insert before / insert after:

insert "Thu" before days[2];
insert "Wed" after days[1];

Per eliminare un elemento da una sequenza possiamo usare l'operatore delete in uno di questi due modi:

delete "Sun" from days;
delete days[0]; // "Mon"

Con delete posso anche rimuovere tutti gli elementi della sequenza in un colpo solo:

delete days;

Con l'operatore reverse posso creare una sequenza dove il primo elemento diventa l'ultimo e viceversa:

def count = [1..5];
var countdown = reverse count; // [5, 4, 3, 2, 1]

Per confrontare due sequenze si può usare l'operatore == che ritorna true solo nel caso le due sequenze abbiano lo stesso numero di elementi, e tutte le coppie di elementi nella stessa posizione siano uguali.

Nell'esempio a seguire le due sequenze sono considerate diverse:

def seq1 = [1,2,3,4,5];
def seq2 = [1,3,2,4,5];
println(seq1 == seq2);

Fare una sequenza a fette:

Con il costrutto seq[a..b] generiamo una partizione di sequenza che include i limiti passati:

def weekend = days[5..6];

Per definire un insieme aperto a destra usiamo seq[a..<>
def weekdays = days[0..<5];>
Possiamo non specificare il limite superiore, la sequenza conterrà tutti gli elementi a partire dal primo specificato con seq[a..]:

def weekend2 = days[5..];

Possiamo anche specificare che vogliamo includere tutti gli elementi a destra dell'elemento specificato tranne l'ultimo della sequenza originaria:

def days2 = days[0..<];

Oggetti e tipi di dato

La terza lezione del tutorial sun su javaFX é dedicata ai concetti di classe e oggetto.

Ecco un esempio di classe fx:

public class Address {
public var street: String;
public var city: String;
public var state: String;
public var zip: String;
}

Il punto e virgola a fine riga é opzionale, ma per chi abbia java nel suo background viene più naturale mettercelo.

Per definire un oggetto di tipo Address farò così:

def myAddress = Address {
street: "1 Main Street";
city: "Santa Clara";
state: "CA";
zip: "95050";
}

Ma lasciamo un attimo in sospeso classi e oggetti e passiamo a vedere i tipi di dato, ovvero la quarta lezione.

String

Una stringa può essere delimitata indifferentemente da singoli o doppi apici, e all'interno di una stringa é possibile quotare espressioni mettendole tra parentesi graffe:

var hi = 'Hello';
def name = "Joe";
var s1 = "{hi} {name}"; // s1 = 'Hello Joe'

A sua volta un espressione contenuta in una stringa può contenere stringhe, e così via:

def answer = true;
var s2 = "The answer is {if (answer) "Yes" else "No"}"; // s = 'The answer is Yes'

Un semplice modo per unire due stringhe é questo:

def one = "This example ";
def two = "joins two strings.";
def three = "{one}{two}";
println(three); // 'This example joins two strings.'

Number e Integer

Ci pensa il compilatore a scegliere il tipo appropriato delle nostre variabili e costanti, e questo vale anche nella scelta tra interi e reali, in ogni caso possiamo esplicitare la scelta dichiarando il tipo di variabile (nell'esempio a seguire, le righe commentate avrebbero lo stesso effetto delle righe precedenti):

def numOne = 1.0; // Number
// def numOne : Number = 1.0;
def numTwo = 1; // Integer
// def numTwo : Integer = 1;

Nota che con javaFX sono diventati disponibili tutti i tipi numerici di java (Byte, Short, Number, Integer, Long, Float, Double, Character), anche se si consiglia di non abusare della nuova libertà.

Boolean

Il tipo Boolean rappresenta due valori: true e false.

var isAsleep = true;

Duration

Usato per specificare una durata in unità temporali. Se ne parlerà meglio più avanti.

5ms; // 5 millisecondi
10s; // 10 secondi
30m; // 30 minuti
1h; // 1 ora

Void

Usato per indicare che una funzione é in realtà una procedura, e quindi non ritorna nulla. Dato che una funzione per default non ritorna nulla, si può fare a meno di indicare che sia di tipo Void (e nota che il tipo é Void con la V maiuscola)

function printMe() : Void {
println("I don't return anything!");
}

null

Invece null si scrive con la n minuscola. Indica la mancanza di un valore normale per una determinata variabile.

PHP Stringhe /2

Continuiamo a lavorare sulle stringhe.

La str_replace() permette di modificare una stringa (subject) sostituendo un dato valore (replace) al posto di una specificata substringa (search). Esiste anche una versione case insensitive, str_ireplace(), che opera la sostituzione senza badare a maiuscole o minuscole:

$input = "Prova, prova ... PROVA!";
echo "Input: \"$input\"<br />";

$output = str_replace("prova", "test", $input);
echo "Ouput: \"$output\"<br />";

$output = str_ireplace("prova", "test", $input);
echo "Ouput: \"$output\"<br />";

Se vogliamo lavorare con stringhe con una lunghezza minima garantita, possiamo usare la str_pad() per aggiungere alla riga, se più corta, caratteri riempitivi. Di default sono aggiunti spazi bianchi alla destra:

$input = "Hi";
echo "[", str_pad($input, 6), "]<br />";
echo "[", str_pad($input, 6, "-", STR_PAD_LEFT), "]<br />";
echo "[", str_pad($input, 6, "_", STR_PAD_BOTH), "]<br />";

E' possibile creare una stringa multiplo di una data, usando str_repeat:

echo str_repeat("echo... ", 4);

Per convertire una stringa in un array usiamo la str_split():

$input = "This is a string";
echo "The input was \"$input\"<br />";

$output = str_split($input);
echo "The output is: ";
$size = count($output);
for($i = 0; $i < $size; ++$i)
echo "[", $output[$i], "] ";

Per estrarre substringhe sapendo l'offset e la lunghezza di quanto si vuole estrarre, si può usare la substr(), che può essere usata anche a partire dal fondo della stringa (indicando un offset negativo). Nota che se non viene specificata la lunghezza della sottostringa, si assume che si voglia arrivare a fine stringa:

$input = "This is a string";
echo "The input was \"$input\"<br />";

echo substr($input, 0, 4), "*";
echo substr($input, 5, 2), "*";
echo substr($input, -6), "*";
echo substr($input, -8, 1);

Per convertire una stringa a tutto maiuscolo o tutto minuscolo si possono usare strtolower() e strtoupper():

$input = "This Is a String";
echo "Given this string \"$input\":<br />";

echo "tolower: ", strtolower($input), "<br />";
echo "toupper: ", strtoupper($input), "<br />";

Si può usare la sscanf() per estrarre delle sottostringhe secondo una particolare formattazione - vedi la printf/scanf del linguaggio C:

$date = "12 May 2009";
list($day, $month, $year) = sscanf($date, "%d %s %d");
echo "Day: $day, month: $month, year: $year.<br />";

Per vedere questi script php in azione vedi qui.

Scrivere script

Seconda lezione dal tutorial Sun su JavaFX, partiamo dicendo qualcosa di più sulle funzioni in questo linguaggio.

Per default, una funzione fx é una procedura, ovvero non ritorna nulla. Dunque le due funzioni che scrivo qui a seguire, hello() e hello2() sono esattamente uguali. Nota che il tipo del parametro tornato dalla funzione viene specificato dopo il nome e la lista dei parametri in input della funzione stessa. E che Void é indicato con la V maiuscola.

function hello() {
println("Hello");
}

function hello2() : Void {
println("Hello");
}

Per mostrare come si passano parametri in input ad una funzione, riscriviamo l'esempio visto nella lezione precedente nel modo a seguire - qui faccio anche la piccola variazione di non usare una variabile globale result in appoggio per la gestione locale delle singole funzioni, ma dichiaro result locale alle funzioni stesse.
Si noti che anche il tipo delle variabili in input alla funzione viene indicato dopo il nome del parametro.

add(100,10);
subtract(50,5);
multiply(25,4);
divide(500,2);

function add(argOne: Integer, argTwo: Integer) {
var result = argOne + argTwo;
println("{argOne} + {argTwo} = {result}");
}

function subtract(argOne: Integer, argTwo: Integer) {
var result = argOne - argTwo;
println("{argOne} - {argTwo} = {result}");
}

function multiply(argOne: Integer, argTwo: Integer) {
var result = argOne * argTwo;
println("{argOne} * {argTwo} = {result}");
}

function divide(argOne: Integer, argTwo: Integer) {
var result = argOne / argTwo;
println("{argOne} / {argTwo} = {result}");
}

function hello() {
println("Hello");
}

function hello2() : Void {
println("Hello");
}

Vediamo come agisce una funzione che accetta due parametri in input di tipo Integer e ritorna un altro Integer. Mi scrivo una nuova funzione addizionatrice che chiamo add2() e la chiamo come da tutorial:

var total = add2(1,300) + add2(23,52);
println("total is {total}");

function add2(argOne: Integer, argTwo: Integer) : Integer {
var result = argOne + argTwo;
println("{argOne} + {argTwo} = {result}");
return result;
}

Il risultato di questo pezzo di codice non dovrebbe essere sorprendente:

1 + 300 = 301
23 + 52 = 75
total is 376

E' lecito chiedersi come funziona la comunicazione dalla shell del sistema al nostro script fx. Dato che non c'é una funzione main, come fare a passargli parametri?

La risposta sta nella funzione run(), che é a tutti gli effetti una procedura, e accetta come parametro in input un array di stringhe.

La usiamo a seguire modificando leggermente il codice sopra visto, per permettere all'utente di effettuare operazioni con i valori che vuole lui.
Faccio un rapido test dell'input, per accertarmi di non avere eccezioni dalla parseInt() e poi chiamo le funzioni di calcolo parametrizzate con i valori passatimi dall'utente.

function run(args : String[]) {
if(args.size() < 2) {
println("Missing input parameter.");
}
else {
// Convert Strings to Integers
def numOne = java.lang.Integer.parseInt(args[0]);
def numTwo = java.lang.Integer.parseInt(args[1]);

// Invoke Functions
add(numOne,numTwo);
subtract(numOne,numTwo);
multiply(numOne,numTwo);
divide(numOne,numTwo);
}
}

Chiamando da shell la mia applicazione:

javafx fxTut.calc2 100 50

ho effettivamente i risultati attesi.

PHP Stringhe /1

Rivediamoci un po' di roba di base per PHP, cominciando da alcune funzioni di uso comune per la manipolazione delle stringhe.

Per calcolare la lunghezza di una stringa usiamo strlen()

int strlen(string);

E dopo il prototipo, ecco un esempio d'uso:

$string = "something";
$length = strlen($string);

echo "The length of \"$string\" is $length<br />";

Per trasformare una stringa in un array di elementi usiamo explode()

array explode(string $delimiter, string $string);

Con questa funzione possiamo trasformare una stringa di CSV (comma separated values - valori separati da virgole) in un array di elementi:

$string = "red,blue,yellow,black,green,purple";
$colours = explode(",", $string);
foreach($colours as $colour)
echo "\"$colour\"<br />";

E viceversa, con la implode() generiamo una stringa a partire da un array di stringhe:

string implode(string $glue, array $elements);

Prendiamo l'array generato dalla precedente chiamata a explode e creiamo una stringa con valori separati da due punti:

$string = implode(":", $colours);
echo "$string<br />";

Eliminiamo gli spazi bianchi (compresi tab, return, nul) ai bordi di una stringa con la famiglia di funzioni trim()

string trim(string);
string ltrim(string); // left trim - elimina gli spazi iniziali
string rtrim(string); // right trim - elimina gli spazi finali

Decidiamo quindi se vogliamo eliminare i soli spazi a destra, a sinistra, o su entrambi i lati della nostra stringa:

$string = "\tblank\r\n";

$trimmed = trim($string);
echo "\"$trimmed\"<br />";

$trimmed = ltrim($string);
echo "\"$trimmed\"<br />";

$trimmed = rtrim($string);
echo "\"$trimmed\"<br />";

Vedi qui il codice sopra descritto in azione.

Primo approccio

Volendo lavorare con javaFX, sono andato sul sito omonimo, mi sono scaricato quanto c'é da scaricare, e sono passato al tutorial get started disponibile sul sito della sun.

Al lavoro, dunque.

Mi sono creato un progetto JavaFX su NetBeans, e un file al suo interno, con la piccola variazione, rispetto a quanto detto nel tutorial, di mettere il file non del default package (orrore!) ma in un apposito package che ho chiamato fxTut.

Il codice sorgente del mio primo file fx é pari pari quanto indicato dal tutorial, pur non avendo mai visto prima nulla scritto in questo linguaggio, le precedenti esperienze in java e perl, tanto per fare un paio di nomi, rendono il codice comprensibile.

Noto che dai linguaggi di scripting fx ha ereditato la mancanza di un main. Il codice viene semplicemente eseguito dalla prima riga in giù.

Molte le somiglianze con java, a partire dalla prima riga, la dichiarazione del package contenente il file (beh, questa é anche l'unica differenza tra il mio codice e quello standard del tutorial).

def evidentemente introduce una costante, var una variabile. Noto che anche qui ci si discosta da java e ci si richiama ai liguaggi di scripting, dato che non c'é tipizzazione esplicita degli oggetti.

Noto infine la println(), un ibrido tra la println java e le funzionalità di stampa dei linguaggi di scripting, con le parentesi graffe a fare da escape per i nomi delle variabili i cui valori vogliamo stampare.

package fxTut;

def numOne = 100;
def numTwo = 2;
var result;

add();
subtract();
multiply();
divide();

function add() {
result = numOne + numTwo;
println("{numOne} + {numTwo} = {result}");
}

function subtract() {
result = numOne - numTwo;
println("{numOne} - {numTwo} = {result}");
}

function multiply() {
result = numOne * numTwo;
println("{numOne} * {numTwo} = {result}");
}

function divide() {
result = numOne / numTwo;
println("{numOne} / {numTwo} = {result}");
}

Fatto correre con un F6 il programma all'interno di Netbeans ottengo il risultato atteso.

Come da tutorial, mi guardo l'esecuzione del mio programma anche da shell. Vado perciò nella directory build del mio progetto ed eseguo:

javafx fxTut.Calc

Dove javafx é la JVM estesa di FX, eseguibile che mi trovo nel bin di javafx-sdk, fxTut é il nome del package dove ho messo il mio codice, Calc é il nome della mio file fx (il sorgente era Calc.fx, qui troviamo il bytecode java Calc.class, l'estensione class non viene esplicitata quando si passa il file alla macchina virtuale, come da solite convenzioni java).

Fatto tutto a modo, il risultato é quello atteso, anche se non esattamente esaltante.

KompoZer

Dovendo scrivere un po' di HTML ed essendomi seccato di fare tutto a mano, ho dato un'occhiata al mercato dei prodotti disponibili nel campo.

Alla fine ho scelto di usare KompoZer, che mi é sembrato il migliore tra quelli che ho visto.

Nonostante sia correntemente ad una versione < 1 (sto usando la 0.8a4, una alpha che mi sembra molto stabile) il prodotto é già piuttosto maturo, grazie al fatto che non nasce dal nulla, ma dall'esperienza di Netscape Navigator/Mozilla Navigator, via Nvu. Un prodotto parente stretto di KompoZer é SeaMonkey.

Non tutto quello che un vorrebbe si fosse e funzionasse al momento c'é e funziona, ma se non si hanno grandi necessità (come nel mio caso), mi pare una buona soluzione.

PDO - prepared statements

In pratica, si tratta di riscrivere la pagina php che abbiamo fatto qui, utilizzando le funzionalità per i prepared statements di PDO dove avevamo usato quelle per mysqli.

Niente di particolare da dire sulla fase di inizializzazione:
if(($dbh = dbo_connect()) == false)
die();
pdo_list_products($dbh);
Più interessante la fase di preparazione della query, perché con PDO é possibile utilizzare parametri identificati da nomi, che rendono più leggibile il codice (dove i punti di domanda utilizzati alternativamente lo rendono più oscuro):
$query = "SELECT sku, name, price FROM products WHERE price < :low";
$stmt = $dbh->prepare($query);
Ora instauriamo un legame tra un variabile PHP e il parametro definito nella query, usando la funzione bindParam() dello statement PDO.

E quindi, per verificare la funzionalità, facciamo un loop for per l'appunto sulla variabile usata.

Eseguiamo lo statement (al primo giro nel loop $low conterrà il valore 2, e via via verrà incrementato) con execute().

Dopo aver fatto l'execute(), possiamo in tutta sicurezza collegare i valori in output dalla nostra select con variabili locali a PHP. In teoria dovrebbe essere possibile farlo anche prima, ma si tratta di un comportamento dipendente dal database sottostante.

Facciamo una rowCount() per verificare che vi sia almeno un articolo nel nostro resultset, se sì, leggiamo con una serie di fetch() tutte le righe disponibili e le visualizziamo sfruttando il binding che abbiamo fatto alle variabili PHP.

Nota che chiamiamo la fetch() con il parametro PDO::FETCH_BOUND, in modo da farle sapere che vogliamo usare le variabili collegate.
$stmt->bindParam(':low', $low);
for($low = 2; $low < 6; ++$low)
{
$stmt->execute();
$stmt->bindColumn(1, $sku);
$stmt->bindColumn(2, $name);
$stmt->bindColumn(3, $price);

if(($stmt->rowCount()) == 0)
{
echo "<br/>No item with a price less than $low €<br/>";
continue;
}

echo "<br/>Items with a price less than $low €:<br/>";
while($row = $stmt->fetch(PDO::FETCH_BOUND))
echo "($sku) $name: €$price<br/>;
}
Qui si può vedere il codice sopra descritto in azione.

PDO exec() e query()

Una volta aperta una connessione a database via PDO, possiamo eseguire statement SQL per mezzo di un paio di funzioni, exec() che esegue lo statement e ritorna il numero di righe che sono state interessate dall'esecuzione dello statement, e query() che ritorna il resultset determinato dall'operazione.

Per prima cosa scriviamo una versione per PDO della funzione che avevamo già scritto per mysqli che visualizza tutte le righe della tabella product, e la mettiamo nella libreria delle nostre funzioni di utilità generale.

Accetta in input un parametro, l'handle PDO al database, chiamo su tale handle la funzione query() per fare la select di tutti i product, e sul resultset risultante faccio una foreach per scandire le righe risultanti (é una simpatica caratteristica della funzione query(), torna un resultset abilitato a gestire iteratori) e mi faccio una printf dei valori trovati:
function pdo_list_products($dbh)
{
$query = "SELECT sku, name, price FROM products ORDER BY id";
foreach($dbh->query($query) as $row)
printf("(%s) %s: €%s<br />", $row['sku'], $row['name'], $row['price']);
}
Fatta questa funzione di libreria, pensiamo alla nostra nuova pagina php. Qui vogliamo fare una update, per cambiare il nome di un nostro articolo in product. Scriviamo perciò una funzione che prende in input l'handle PDO e il nuovo nome che vogliamo dare all'articolo con codice ZP457321 (ammetto che é una funzione un po' poco realistica, ma va bene per lo scopo dimostrativo di questo post).
Eseguendo lo statement sql di update via exec() abbiamo in output in numero di righe effettivamente modificate dalla update, informazione che rendiamo disponibile all'utente:
function update_aftershave($dbh, $name)
{
$query = "UPDATE products SET name='".$name."' WHERE sku='ZP457321'";
$affected = $dbh->exec($query);
echo "<p>Total rows affected: $affected</p>";
}
E questo a seguire é il corpo principale del nostro script php. Ci connettiamo a PDO, tramite la funzione di libreria già vista nel post precedente, facciamo un primo printout della tabella con la nuova funzione di libreria pdo_list_products(), cambiamo una prima volta in nome del nostro articolo, e visualizziamo la nuova situazione a database, cambiamo una seconda volta il nome dell'articolo, e visualizziamo per l'ultima volta lo stato delle cose a database:
if(($dbh = dbo_connect()) == false)
die();

pdo_list_products($dbh);

update_aftershave($dbh, "Painful Aftershave");
pdo_list_products($dbh);

update_aftershave($dbh, "Painless Aftershave");
pdo_list_products($dbh);
Si veda qui il codice sopra descritto in azione.

Pagine parametrizzate via post http

Vediamo come gestire dati passati ad una pagina PHP via POST HTTP.

Per prima cosa scriviamo una paginetta html con un form che fa una post di un paio di variabili verso un'altra pagina, la nostra pagina php che li gestirà.

Notiamo pigiando il bottone "chiedi" il controllo verrà passato alla pagina response.php con i valori dei due campi di input name e question come specificati dall'utente.
<form action="response.php" method="post">
<p>
Il tuo nome:<br /><input type="text" name="name" /><br />
La tua domanda:<br /><input type="text" name="question" />?
</p>
<input type="submit" value="chiedi" />
</form>
Vediamo ora il codice PHP che caratterizza la response.php.

Per prima cosa facciamo un controllo su le variabili passate alla pagina, via POST HTTP, ovvero, controlliamo che l'array associativo $_POST[] sia definito e contenga le due variabili name e question che ci aspettiamo.

Se qualcosa va storto con $_POST[], ovvero probabilmente la pagina del responso é stata chiamata direttamente e non dalla pagina che abbiamo scritto sopra per generare il quesito secondo la nostra impostazione, diciamo all'utente che non abbiamo capito cosa vuole.

Nel caso contrario verifichiamo che le due variabile postate non siano vuote. Se lo sono, immettiamo un valore di default.

Mostriamo all'utente a cosa stiamo per rispondere, e quindi passiamo al cuore del nostro meccanismo per la generazione dell'oracolo: la funzione rand() che genera un valore intero nell'intervallo passato, nel nostro caso, zero o uno. A seconda del risultato visualizziamo un Sì o un No.
if($_POST && isset($_POST["name"]) && isset($_POST["question"]))
{
$name = $_POST["name"];
if(strlen($name) == 0)
$name = "Anonimo";

$question = $_POST["question"];
if(strlen($question) == 0)
$question = "É uscito testa";

echo $name, ", mi hai chiesto: ", $question, "?<br />";
echo "<p>La mia risposta é: ";
echo (rand(0,1) == 0) ? "<b>Sì</b></p>" : "<b>No</b></p>";
}
else
{
echo "<p>Non ho capito la tua domanda.</p>";
}
Come tocco finale, mettiamo un link alla pagina con la domanda, in modo da permettere al nostro utente di fare una nuova domanda:
<a href=".">Clicca qui per fare un'altra domanda</a>.
E questo é tutto.

Si può vedere il codice in azione in queste pagine: qui la generazione della domanda, qui la generazione della risposta.

PDO - PHP Data Object

OK, la connessione tra PHP e MySql é efficace, sperimentata, ben nota a tutti.

Però non é mai una buona idea legarsi ad un solo database. Anche se oggi scriviamo un pezzo di codice PHP che sappiamo fa riferimento a un database MySql, mica é detto che questo valga anche domani. E doversi riscrivere tutto il codice che gestisce l'interfacciamento al database sarebbe una bella seccatura.

E' per questo che esistono un gran numero di soluzioni che contribuiscono a mantenere indipendente il codice dal database utilizzato. Un esempio per tutti: JDBC, la soluzione standard java al problema.

La soluzione standard PHP, dalla versione 5, si chiama PDO, PHP Data Object.

Per default non é attiva, e quindi la prima cosa che dobbiamo fare, se vogliamo usare PDO, é cambiare php.ini, fortunatamente in un modo molto semplice: normalmente basta decommentare un paio di righe, una per le funzionalità generiche del PDO e una per l'estensione relativa al database che intendo utilizzare.

Nel nostro caso (il server corre su windows, vogliamo usare MySql) dobbiamo accertarci che queste due righe siano presenti (e senza un punto e virgola come primo carattere) nel nostro file di inizializzazione:
extension=php_pdo.dll
extension=php_pdo_mysql.dll
Dopodichè basta ristartare il server Apache per avere a disposizione le nostre funzionalità aggiuntive.

Ora, nel file in cui vogliamo accedere al database, dobbiamo crearci un oggetto PDO. E' possibile far ciò in un gran numero di modi, ad esempio posso passare i parametri di inizializzazione al costruttore, una cosa di questo tipo:
$dbh = new PDO("mysql:host=localhost;dbname=test", "user", "password");
Essendo PDO un oggetto, ci comunica se qualcosa non va lanciando un eccezione. Sommando questo al fatto che vogliamo spostare la funzionalità di connessione in un altro file, in modo che sia visibile da tutte le pagine che vogliamo abbiano accesso al database senza stare a riscrivere i parametri di connessione in giro per il nostro progetto, giungiamo alla conclusione di scrivere una funzione, che chiameremo dbo_connect().
Non richiede parametri in input, ritorna l'handle al database in caso di successo, o false se qualcosa va storto.
Nota come l'eccezione (per la precisione, una PDOException) generata dal costruttore in caso di errore venga gestita localmente, segnalando all'utente l'errore.
function dbo_connect()
{
try
{
return new PDO("mysql:host=localhost;dbname=test", "user", "password");
}
catch(PDOException $e)
{
echo "Exception connecting to database: ", $e->getMessage(), "<br />";
return false;
}
}
A questo punto, una semplice paginetta web che non fa null'altro che tentare di connettersi al database e visualizzare un messaggio di successo se tutto va bene, avrà il seguente codice PHP:
if(($dbh = dbo_connect()) == false)
die();
echo "PDO connection succeeded.";
Per vedere in azione il codice sopra descritto, basta andare qui.

Prepared statements

In questo post facciamo un esempio di uso di prepared statements.

L'idea é quella di salvare il tempo necessario per eseguire una query preparandola per quanto possibile in anticipo, limitandosi a mandare al database i parametri che cambiano tra una esecuzione e l'altra.

Primo passo, il solito startup, come già visto nei precedenti post:
require_once("utils.php");

$mysqli = db_connect();
list_products($mysqli);
Adesso mi creo una query parametrizzata. In questo caso una semplice select sulla nostra tabella products che ritorna codice, nome e prezzo di tutte gli articoli registrati che abbiano un prezzo inferiore a un valore che vogliamo specificare di volta in volta.

Mi creo uno statement, usando la funzione stmt_init() e lo preparo per la mia query, chiudendo brutalmente la sessione se fallisco nel tentativo:
$query = "SELECT sku, name, price FROM products WHERE price < ?";  $stmt = $mysqli->stmt_init();
if(($stmt->prepare($query)) == false)
die("Can't prepare statement - execution aborted.");
Adesso leghiamo i parametri a variabili locali. Ogni punto di domanda nella query verrà abbinato a una variabile php per mezzo di bind_param(). Il primo parametro di questa funzione é il tipo di variabile che vogliamo usare (d sta per numero reale (decimal), s per stringa, etc.), segue la lista di tutte le variabili php utilizzate. Nel nostro caso é semplice, abbiamo solo una variabile da associare, di tipo "d".
$stmt->bind_param("d", $low);
Nel ciclo for che segue variamo il nostro parametro associato, e vediamo che succede.
Per prima cosa eseguiamo lo statement, poi facciamo una store_result() per avere a disposizione l'intero resultset risultante. Dato che sappiamo che ci tornano poche righe di tabella, questa é l'opzione migliore, che riduce gli accessi al database e non rischia di allocare grosse quantità di memoria inutilmente.
Inoltre, avendo chiamato la store_result() possiamo chiamare la num_rows() che ci dice quante righe abbiamo effetivamente selezionato. Se non ne abbiamo nemmeno una, passiamo alla successiva iterazione del ciclo for.
Altrimenti connettiamo i valori ritornati dalla select con variabili php per mezzo della bind_result(), e li mostriamo al nostro utente.
for($low = 2; $low <>execute();
$stmt->store_result();
if(($stmt->num_rows()) == 0)
{
echo "<br/>No item with a price less than $low €<br/>";
continue;
}

$stmt->bind_result($sku, $name, $price);

echo "<br/>Items with a price less than $low €:<br/>";
while ($stmt->fetch())
echo "($sku) $name: €$price<br/>";
}
Manca ora solo il cleanup. Chiudiamo lo statement, e la connessione al database.
$stmt>close();
db_close($mysqli);
Qui si può vedere il risultato di queste operazioni.