domenica 13 maggio 2012

Sessioni PHP: cosa sono, come si usano

Pubblicato su HTML.it un mio articolo con qualche trucco non banale per una corretta configurazione delle sessioni in PHP Sessioni PHP: cosa sono, come si usano

martedì 27 marzo 2012

Distribuzione delle sessioni su più server in load balancing

La distribuzione delle sessioni è un problema che riguarda la scalabilità dei siti web su più server, in particolare per PHP che di norma salva le sessioni su file system del singolo server. In queste slides vedremo alcune strategie di risoluzione del problema (load balancing mirato, accentramento delle sessioni) e ci soffermeremo su una personale soluzione che prevede la distribuzione delle sessioni su più macchine contemporaneamente creando un’architettura a maglia attraverso l’uso di memcache.

lunedì 10 agosto 2009

La persistenza degli webservices

Quando si implementa un server Soap occorre fare attenzione al fatto che per ogni metodo invocato dal client il nostro script server verrà sempre riavviato (e la classe verrà re-istanziata)

Quindi se il nostro server è

< ?

class Doppio {
private $doppio;
public function set($numero){
$this->doppio=$numero;
}

public function get() {
return $this->doppio*2;
}
}



$server = new SoapServer( 'doppio.wsdl' );
$server->setClass('Doppio');
$server->handle();

?>



Ed il client è

< ?
$a = new SoapClient("http://127.0.0.1/Adv/xml/doppio.wsdl");
$a->set(5);
echo $a->get();

?>

Restituirà 0 perché ogni istruzione sarà vista come un lancio di script, per ovviare a questo inconveniente inserire nel server l’istruzione setPersistence

Così:

< ?

class Doppio {
private $doppio;

function __construct() {
$this->doppio=$_SESSION['doppio'];
}

function __sleep() {
$_SESSION['doppio']=$this->doppio;
}

. . . . codice precedente . . . .
}


session_start();
$server = new SoapServer( 'doppio.wsdl' );
$server->setClass('Doppio');
$server->handle();

?>




< ?
. . . . codice precedente . . . .

$server = new SoapServer( 'doppio.wsdl' );
$server->setClass('Doppio');
$server->setPersistence(SOAP_PERSISTENCE_SESSION);
$server->handle();
?>

Ma attenzione perché questa è una persistenza basata su sessioni quindi lo script viene sempre eseguito due volte ma tra l’una è l’altra l’oggetto viene salvato nella sessione

lunedì 27 luglio 2009

Webservices più veloci

Per velocizzare web services che restituiscono grosse quantità di dati (che si amplificano per l’effetto dell’XML in cui sono “imbustati”) può essere utile usare la compressione gzip nelle risposte del server
Es.

Client
< ?
$a = new SoapClient('myservice.wsdl',
array('compression' => SOAP_COMPRESSION_ACCEPT SOAP_COMPRESSION_GZIP)
); //abilitiamo la compressione GZIP per le risposte
..resto dello script

?>


Server

< ?
.. inclusione classi ...
$server = new SoapServer( 'myservice.wsdl' );
$server->setClass('myservice');

if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip')===false)
$server->handle();
else {
ob_start();
$server->handle();
$x=gzencode(ob_get_clean(), 7); //7 è più che sufficiente per non perdere tempo in compressione
header("Content-Encoding: gzip");
header("Content-Length: ".strlen($x));
echo $x;
}

/* in alternativa si potrebbe catturare con ob_start tutto l’output sempre salvo comprimerlo se il client lo supporta ma anche se la dimensione supera i 1024 bytes ad esempio */

?>

venerdì 15 maggio 2009

Aguzza la vista

Perché questo script:

<?php
session_start();
?>



restituisce
Warning:: session_start() Cannot send session cookie - headers already sent by (output started at ...:1) ?

La sessione è basata sull’invio di un cookie, per l’appunto cookie di sessione, com’è noto i cookie si inviano nell’header dell’http quindi non si può inviare quel cookie attraverso il session_start() se prima si è fatta una echo.. ma non è stata fatta nessuna echo... ed invece si.

Si può notare (con molta attenzione ;-)) che prima dell’apertura del <?php è presente un'interlinea, che sia uno o un’infinità di caratteri quello è comunque un invio di testo che comporta la chiusura dell’header e l’impossibilità di inviare successivamente il cookie di sessione.

Sembra una sciocchezza ma non è raro che si perda tempo per un carattere sfuggito, magari all’inizio di uno script incluso.

lunedì 11 maggio 2009

IP e MAC address del server

Ecco un trucco per recuperare il/i macaddress e relativo ip del server usando un’istanza COM (ovviamente su Windows)


<?
$wmi = new COM("WinMgmts:{impersonationLevel=impersonate}");
$IPConfigSet = $wmi->ExecQuery("SELECT Description,MACAddress,IPAddress
     FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True");
$out=array();
foreach ($IPConfigSet as $i=>$IPConfig )
  foreach($IPConfig->IPAddress as $ip)
    if ($ip!='0.0.0.0')
     echo $ip,' | ',$IPConfig->MACAddress,' | ',$IPConfig->Description,"\n";

?>


Ed il risultato sarà della forma
172.20.10.58 | 00:11:2F:B9:12:1D | Intel(R) PRO/100 VE Network Connection - Miniport dell'Utilità di pianificazione pacchetti

venerdì 17 aprile 2009

Usare i Prepared Statements con le librerie Mysql

Com’è noto i prepared statement non sono supportati da mysql, (vedi anche io mio post "Mysql contro Mysqli" del 14/4/2009) ma sfruttando le istruzioni PREPARE ed EXECUTE del SQL di mysql ecco una funzioncina in grado di usarli; inoltre questi prepared statements possono essere riutilizzati da altri script che agganciano la stessa connessione persistente in quanto associati ad un nome calcolato in base alla query.
Naturalmente in questi statementes non c' binding con variabili del PHP ma sono utili per query particolarmente complicate o (meglio ancora) di aggiornamento del DB.



function mysql_query_prepared($sql, $parametri=false){
if (!is_array($parametri)) return mysql_query($sql); //uso standard
else {
$sql= str_replace(array("\r","\n","\t"),array('','',''), trim($sql)); //pulisce sql
$stmt_name="PS".md5($sql); //calcola un nome per il prepared statement
if (count($parametri)>0)
{//$parametri non vuoto, trasmette i parametri al server come variabili
foreach ($parametri as $k=>&$v) $vars["@A$k"]="@A$k:='".mysql_escape_string($v)."'";
mysql_query("set ".implode(",",$vars));
$usa_variabili=' using '.implode(',',array_keys($vars));
}

//Prova ad eseguire lo statemend direttamente
$query_stmt="execute $stmt_name $usa_variabili";
$esito=mysql_query($query_stmt);
if (mysql_errno()==1243)
{
//se non esiste uno statamente $stmt_name prova a crearlo e se ci riesce lo usa
if (mysql_query($x="prepare $stmt_name from '".mysql_escape_string($sql)."'"))
$esito=mysql_query($query_stmt);
else {//se non riesce a crearlo effettua una sostituzione manuale dei parametri
if ($parametri)
foreach (explode('?',$sql) as $i=>$parte)
$newSql.="$parte ".($parametri[$i]?"'".mysql_escape_string($parametri[$i])."'":'');
$esito=mysql_query($newSql); //e lo esegue normalmente
}
}
return $esito;
}
}


//Possibili usi
mysql_query_prepared("select * from persone where id=?", array(120)); //usa un PS parametrizzato
mysql_query_prepared("select * from persone where id=120", array()); //usa un PS non parametrizzato
mysql_query_prepared("select * from persone where id=120"); //utilizzo classico