Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Gestire le risorse

Arricchire le nostre estensioni con le risorse
Arricchire le nostre estensioni con le risorse
Link copiato negli appunti

Le risorse rappresentano un tipo di dato molto importante in PHP, utilizzato quando è necessario mantenere qualche tipo di informazione che non può essere esposta a PHP. Per esempio le funzioni per la gestione della immagini, quelle per l'interrogazione dei database e le funzioni per la creazione di parser SAX sfruttano le risorse.

Lo Zend Engine salva le risorse in una lista interna che viene interrogata utilizzando un identificatore associato alla risorsa per recuperare il tipo di dato base. Quando la risorsa non ha più riferimenti, il motore di PHP si occupa di richiamare una funzione distruttore che solitamente libera la memoria allocata precedentemente. Il distruttore deve essere registrato in modo che PHP conosca quale funzione richiamare per liberare una risorsa. È da precisare che PHP permette l'utilizzo di due tipi di risorse: le risorse normali, le quali vengono distrutte quando lo script che le ha create termina o quando l'engine viene fermato manualmente, e le risorse persistenti, che persistono in memoria finché non vengono distrutte esplicitamente o finché l'engine non viene terminato manualmente.

Procediamo con un semplice esempio che mostra una libreria utilizzata per leggere dei dati da un file utilizzando le API del sistema operativo. È da notare che l'esempio utilizza una variabile statica in cui contenere il riferimento alla risorsa. Questa operazione è corretta per sistemi a thread singolo. I sistemi multithread su cui viene eseguito PHP condividerebbero la stessa variabile per ogni risorsa creata, generando problemi a livello di esecuzione. Per una corretta implementazione sarebbe necessario utilizzare il sistema per la gestione della variabili globali, descritto nel paragrafo 11.

void html_it_file_destruction_handler(zend_rsrc_list_entry *rsrc TSRMLS_DC);
typedef struct _html_it_file_resource
{
   FILE *fp;
}html_it_file_resource;

#define RESOURCE_NAME "Html.it File Resource"
static int le_html_it_file;

PHP_MINIT_FUNCTION(html_it_file)
{
   le_html_it_file = zend_register_list_destructors_ex(html_it_file_destruction_handler, NULL, RESOURCE_NAME, module_number);

   return SUCCESS;
}

void html_it_file_destruction_handler(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
    html_it_file_resource* resource;

   resource = (html_it_file_resource*)rsrc->ptr;
   fclose(resource->fp);
   efree(resource);
}

PHP_FUNCTION(html_it_open)
{
   html_it_file_resource* resource;
   int id;
   int argc = ZEND_NUM_ARGS();
   int input_len;
   char* input = NULL;

   if(zend_parse_parameters(argc TSRMLS_CC, "s", &input, &input_len) == FAILURE)
   {
      return;
   }

   resource = emalloc(sizeof(html_it_file_resource));
   resource->fp = fopen(input, "r");
   id = ZEND_REGISTER_RESOURCE(return_value, resource, le_html_it_file);

   RETVAL_RESOURCE(id);
}

PHP_FUNCTION(html_it_read)
{
   html_it_file_resource* resource;
   zval* arg;
   char buffer[1024];
   int argc = ZEND_NUM_ARGS();

   if(zend_parse_parameters(argc TSRMLS_CC, "r", &arg) == FAILURE)
   {
      return;
   }

   ZEND_FETCH_RESOURCE(resource, html_it_file_resource*, &arg, -1, RESOURCE_NAME, le_html_it_file);

   if(resource)
   {
      if(fgets(buffer, 1024, resource->fp))
      {
         RETURN_STRING(buffer, 1);
      }else
      {
         RETURN_NULL;
      }
   }
}

PHP_FUNCTION(html_it_close)
{
   zval* arg;
   int argc = ZEND_NUM_ARGS();

   if(zend_parse_parameters(argc TSRMLS_CC, "r", &arg) == FAILURE)
   {
      return;
   }

   zend_list_delete(Z_RESVAL(*arg));
}

L'estensione espone tre funzioni che si occupano, nell'ordine, di aprire, leggere e chiudere un file utilizzando le API fornite dal sistema operativo. La prima riga definisce il prototipo del distruttore della risorsa. Tutti i distruttori dovranno seguire questo prototipo, anche se potranno fornire nomi differenti. Successivamente viene definito un nuovo tipo di dato che rappresenta la nostra risorsa: il nuovo tipi di dato non è altro che una struttura contenente un puntatore ad un file aperto. Viene anche definita una variabile statica che conterrà il tipo associato dallo Zend Engine alla risorsa nel momento in cui questa verrà registrata. La funzione definita utilizzando la macro PHP_MINIT_FUNCTION (discussa nei paragrafi successivi) viene richiamata in automatico da PHP durante il processo di inizializzazione. Nel nostro caso si occupa di registrare la risorsa da noi definita in modo che possa essere utilizzata dal modulo. La risorsa viene registrata utilizzando la funzione zend_register_list_destructors_ex che accetta i seguenti parametri:

  • un puntatore alla funzione utilizzata per liberare la risorsa nel caso questa sia di tipo normale. Ricordo che la funzione passata deve seguire il prototipo specificato all'inizio del file;
  • un puntatore al distruttore che si occuperà di liberare la risorsa nel caso questa sia di tipo persistente. Può avere anche valore nullo, come nel nostro caso;
  • una stringa che specifica il nome univoco da associare alla risorsa;
  • un valore interno utile allo Zend Engine per operare la registrazione correttamente. Le funzioni definite con la macro PHP_MINIT_FUNCTION espongono il parametro module_number, che può essere usato come argomento.

Una volta operata la registrazione ci viene restituito un intero che rappresenta l'identificativo univoco del nuovo tipo di risorsa. Nella funzione html_it_open viene aperto il file che si trova al path passato come argomento (ricordo che le funzioni sono solo d'esempio, e non dovrebbero essere utilizzate in un contesto reale che necessiterebbe un controllo sulla sicurezza e per lo meno sui parametri passati). Il puntatore al file viene salvato nella struttura da noi definita, che poi viene trasformata in una risorsa ed assegnata al valore di ritorno utilizzando ZEND_REGISTER_RESOURCE che accetta come parametri la destinazione, un puntatore alla risorsa e l'indice del tipo di risorsa registrato precedentemente.

Nella funzione html_it_read viene effettuata la lettura di una porzione di testo dal file. La funzione accetta come parametro la risorsa da utilizzare, che viene salvata in arg . Al fine di recuperare dallo zval passato come argomento la risorsa a noi necessaria, viene utilizzata la macro ZEND_FETCH_RESOURCE che accetta i seguenti parametri:

  • un puntatore alla risorsa sulla quale opereremo. Deve essere del tipo di dato registrato precedentemente;
  • il tipo di dato associato alla risorsa;
  • l'indirizzo dello zval contenente l'ID della risorsa a cui fa riferimento l'argomento passato;
  • l'ID di default utilizzabile se la risorsa non può essere recuperata;
  • il nome della risorsa;
  • l'indice associato al tipo di risorsa registrato precedentemente;

La macro si occupa di verificare che la risorsa sia corretta: in caso non lo sia si occupa da sola di restituire il controllo allo script chiamante restituendo NULL. Dopo la chiamata a questa macro, è possibile operare correttamente su resource.

L'ultima funzione si occupa di chiudere la risorsa, eliminandola dalla lista interna delle risorse delle zend engine attraverso la funzione zend_list_delete, che accetta come parametro l'indice della risorsa da eliminare. La chiamata a questa funzione eseguirà di conseguenza il distruttore associato alla risorsa, che si occuperà di chiudere il file aperto e di eliminare la memoria allocata.

Ti consigliamo anche