Lezione

4. Tuning PHP

4. Tuning PHP

Nel manuale 1 abbiamo proceduto all’installazione di PHP in una forma base lenta e senza alcune importanti configurazioni.

Va fatto subito presente che alcune impostazioni sono specifiche solo per alcuni siti. Pertanto è saggio (o doveroso) vedere i requisiti specifici del singolo prodotto che intendiamo usare.

Dunque vediamo l’architettura di PHP, i settaggi assolutamente da fare, il file .htaccess e, in fine, attiviamo PHP-FPM al posto del modulo base PHP.

4.1. Architettura

PHP può essere configurato e settato da più punti.

Fondamentalmente declina la doppia logica di una stratificazione dei permessi da un controllo centrale che può delegare tutto o qualcosa agli altri livelli.

Questa complessità, difficile da comunicare in una sola frase, deriva da un’architettura pensata per un server, dove l’amministratore deve poter tenere il controllo, dove sono ospitati più siti realizzati con applicativi diversi che impiegano configurazioni diverse e dove può essere necessario autorizzare un parametro custom in una sola directory in un sito particolare.

Insieme a questo scenario dobbiamo aggiungere un’altra complicazione. Infatti PHP può essere impiegato come linguaggio nel web server, ma può essere anche un linguaggio usato da riga di comando in background, fuori dalle pagine web (è il caso, ad esempio, di processi cron o la CLI di WordPress o di NextCloud).

Detto ciò PHP mette tutte le configurazioni centrali nel file php.ini accessibile e modificabile solo all’amministratore del server.

Il php.ini lo troviamo in tre path: /etc/php/8.1/apache2 , /etc/php/8.1/cli e /etc/php/8.1/cgi :

  • /etc/php/8.1/apache2 : sono i settaggi adottati quando PHP viene invocato come linguaggio dal software del sito (es. WordPress o Joomla);

  • /etc/php/8.1/cli : sono i settaggi adottati quando PHP viene invocato da CLI come nel caso di processi CRON o dei web services come wp-cli di WordPress od occ di NextCloud;

  • /etc/php/8.1/cgi : sono i settaggi adottati quando PHP viene invocato come script CGI.

A valle di questi primi settaggi, gestibili esclusivamente dall’amministratore, abbiamo la possibilità di impostare dei settaggi tramite le configurazioni del web server Apache, ovvero il file example.com.conf che abbiamo creato nel manuale 1. Se abbiamo creato un certificato con CertBot, come descritto nel manuale 1, il file è 001-example.com-le-ssl.conf . In questa fase quando l’utente invoca una pagina web, Apache passa il codice sorgente a PHP (se è codice PHP) filtrando il codice stesso e, in modo limitato, ridimensionando alcuni parametri di runtime di PHP. Tramite Apache possiamo passare parametri diversi in base alla directory-file invocato e possiamo definire, con una granularità di dettaglio, cosa può controllare l’utente che ha il permesso di scrivere nello spazio web.

A valle del controllo fatto dal server Apache è possibile possibile controllare ulteriormente il comportamento di PHP tramite il file .htaccess scritto all’interno delle directory dello spazio web (con l’attività fatta nel manuale 1 coincide con il path /var/www/example.com/html ). Le possibilità di manipolare le impostazioni di PHP sono limitate da quanto definito nelle configurazioni di Apache e, prima ancora, dalle impostazioni in php.ini . Inoltre le impostazioni in .htaccess si applicano solo alla directory in cui si trova e alle eventuali sotto-directory. Ad esempio:

html/a/.htaccess
html/a/a1
html/a/a2
html/b
html/b/b1
html/b/b2

le impostazioni di .htaccess si applicano solo ai file PHP contenuti nella directory “a” e alle sottodirectory “a1” e “a2”, ma non al ramo “b”.

Questa architettura richiede un po’ di studio per apprenderla, ma risponde a una logica lineare che permette di mantenere un controllo centrale, ma di dare anche spazio di modifiche all’utente con diritti di scrittura sul server o alle applicazioni stesse.

4.2. Settaggi

All’installazione PHP ha un’impostazione molto limitata, con dimensioni piccole per l’upload di file, piccola RAM e inibite le autorizzazioni di efficacia per .htaccess .

Tra i parametri da impostare ci sono le direttive:

  • memory_limit : imposta la quantità massima di RAM usabile da uno script. Solitamente è fissata a 128MB;

  • post_max_size : indica la dimensione massima di un pacchetto inviato da un client. Per pacchetto possiamo avere un file (operazione di upload) o i dati inviati tramite un form;

  • sys_temp_dir : directory dove vengono messi file temporanei creati da PHP. Di default non è impostata e PHP usa solo la RAM;

  • upload_max_filesize : dimensione massima accettata per file inviati in upload. Di default è fissata a 2MB;

  • date.timezone : imposta il fuso orario di riferimento di PHP. Di default non è impostato, ma per svariate ragioni è opportuno impostarlo settandolo sul fuso orario dove si trova il server. L’elenco delle timezone supportate da PHP è disponibile all’URL https://www.php.net/manual/en/timezones.php ;

  • apc.enabled : si tratta di una cache “Alternative PHP Cache” di default presente in PHP e settata con dei valori base.

Passiamo a imposta e attivare queste impostazioni. Teniamo presente che il php.ini di apache ha impostazioni di default molto più limitate rispetto al php.ini di CGI o di CLI. Questo perché il rischio ed il bisogno di sicurezza delle pagine web esposte da Apache è molto più acuto di CGI e di CLI. Pertanto le configurazioni sono diverse in ciascun scenario d’uso.

Prima di iniziare l’aggiornamento torniamo al lavoro fatto nel manuale 1 con l’installazione di PHP. Assicuriamoci di avere sulla webroot del dominio example.com il file /var/www/example.com/html/info.php con il contenuto

<?php
    phpinfo();
?>

e tramite il nostro web browser accediamo all’URL http://www.example.com/info.php .

Suggeriamo di tenere aperta questa pagina mentre si procede con l’aggiornamento.

Iniziamo l’aggiornamento delle impostazioni di PHP editando /etc/php/8.1/apache2/php.ini

sudo nano /etc/php/8.1/apache2/php.ini


[ ... ]
memory_limit = 256M
[ ... ]
post_max_size = 64M
[ ... ]
sys_temp_dir = "/tmp"
[ ... ]
upload_max_filesize = 63MB
[ ... ]
date.timezone = Europe/Rome
[ ... ]
[apc]
;
; Stringhe non presenti da aggiungere alla fine di php.ini
; configurazione custom di APCu
;
apc.enabled=1
apc.shm_segments=1
apc.shm_size=32M
apc.entries_hint=4096
apc.ttl=1024
apc.gc_ttl=3600
apc.mmap_file_mask=
apc.slam_defense=1
apc.enable_cli=1
apc.use_request_time=1
apc.serializer=php
apc.coredump_unmap=0
; apc.preload_path=

Aggiorniamo /etc/php/8.1/cli/php.ini

sudo nano /etc/php/8.1/cli/php.ini


[ ... ]
post_max_size = 64M
[ ... ]
sys_temp_dir = "/tmp"
[ ... ]
upload_max_filesize = 63MB
[ ... ]
date.timezone = Europe/Rome
[ ... ]
[apc]
;
; Stringhe non presenti da aggiungere alla fine di php.ini
; configurazione custom di APCu
;
apc.enabled=1
apc.shm_segments=1
apc.shm_size=32M
apc.entries_hint=4096
apc.ttl=1024
apc.gc_ttl=3600
apc.mmap_file_mask=
apc.slam_defense=1
apc.enable_cli=1
apc.use_request_time=1
apc.serializer=php
apc.coredump_unmap=0
; apc.preload_path=

Aggiorniamo /etc/php/8.1/cgi/php.ini

sudo nano /etc/php/8.1/cgi/php.ini


[ ... ]
memory_limit = 256M
[ ... ]
post_max_size = 64M
[ ... ]
sys_temp_dir = "/tmp"
[ ... ]
upload_max_filesize = 63MB
[ ... ]
date.timezone = Europe/Rome
[ ... ]
[apc]
;
; Stringhe non presenti da aggiungere alla fine di php.ini
; configurazione custom di APCu
;
apc.enabled=1
apc.shm_segments=1
apc.shm_size=32M
apc.entries_hint=4096
apc.ttl=1024
apc.gc_ttl=3600
apc.mmap_file_mask=
apc.slam_defense=1
apc.enable_cli=1
apc.use_request_time=1
apc.serializer=php
apc.coredump_unmap=0
; apc.preload_path=

Dopo aver apportato questi aggiornamenti dobbiamo attivarlo. Possiamo farlo comunicando ad Apache di ricaricare le configurazioni. In questo caso non abbiamo interruzioni di servizio, ma con alcune configurazioni avanzate o particolari potremmo non vedere gli effetti. In particolare il ricaricamento delle configurazioni non azzera le eventuali cache precedentemente create e dovremmo attendere il timeout di scadenza delle stesse.

Il comando da dare è

sudo systemctl reload apache2

Oppure possiamo riavviare completamente Apache. Questo azzera tutto (cache comprese), ma introduce un’interruzione di servizio e l’operazione richiede qualche secondo in più.

Il comando da dare è

sudo systemctl restart apache2

Verifichiamo gli aggiornamenti apportati.

Tramite il nostro web browser, apriamo una nuova finestra o un nuovo tab e accediamo all’URL http://www.example.com/info.php ; confrontiamo i valori presenti sulla finestra (o sul tab) prima dell’aggiornamento e quelli presenti dopo l’aggiornamento. Utile cercare in tutta la pagina tramite le parole chiave (ad esempio: post_max_size ) avere un’immediata evidenza del lavoro fatto.

4.3. .htaccess

La sintassi accettata in questo file è un po’ diversa da quella che abbiamo visto sopra perché parla direttamente con Apache. A seconda della direttiva viene elaborata direttamente da Apache o passata al programma responsabile per quella direttiva.

Premesso ciò tutto diventa più facile.

Per quanto riguarda PHP vedremo che le parole chiave (o parole riservate) sono quasi identiche.

Per rendere attivo questo file dobbiamo agire sulla configurazione di Apache. Riprendendo il lavoro fatto con il manuale 1 . Editiamo il file example.com.conf . Se avessimo attivato l’HTTPS tramite CertBot dobbiamo operare sul file 001-example.com-le-ssl.conf . Assicuriamoci che ci sia la direttiva AllowOverride All per la webroot:

        <Directory "/var/www/example.com/html">
                Options Indexes FollowSymLinks
                AllowOverride All
                Require all granted
        </Directory>

Nota 1: il valore All rende modificabili tutte le impostazioni predefinite di PHP. Il sistemista, in alternativa, può indicare una o più chiavi modificabili dal file .htaccess in modo da limitare le possibilità di manipolazione;

Nota 2: la direttiva <Directory "/var/www/example.com/html"> rende attivo .htaccess a tutto il contesto del dominio www.example.com . Se, ad esempio, avessimo configurato la direttiva per la sottodirectory “a

        <Directory "/var/www/example.com/html/a">
                Options Indexes FollowSymLinks
                AllowOverride All
                Require all granted
        </Directory>

l’override concesso ad .htaccess si applica solo alla sotto-directory “a” e alle eventuali ulteriori directory contenute in “a”. In questo caso .htaccess deve trovarsi dentro la directory “a”, ovvero nel path /var/www/example.com/html/a .

Normalmente il file .htaccess viene caricato dall’operatore che ha i diritti per usare l’SFTP per l’upload dei file.

Molti CMS o altri applicativi LAMP (es.: vTiger, PHPlist, ecc…) hanno già dei file .htaccess all’interno del pacchetto che impostano i corretto dimensionamento delle direttive PHP.

Ecco qualche riga di esempio tratte da un file .htaccess di un’installazione di una vecchia versione di Roundcube webmail ( https://roundcube.net ), un’applicazione di webmail:

# AddDefaultCharset     UTF-8
AddType text/x-component .htc

php_value       upload_max_filesize     5M
php_value       post_max_size           6M
php_value       memory_limit            64M

php_flag        session.auto_start      Off
php_value       session.gc_maxlifetime  21600
php_value       session.gc_divisor      500
php_value       session.gc_probability  1

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^favicon\.ico$ skins/larry/images/favicon.ico
</IfModule>

4.4. PHP-FPM

Ritorniamo sul lavoro fatto nel manuale 1 dove abbiamo installato e attivato la modalità base di PHP.

Pur essendo perfettamente funzionante il modulo PHP di Apache è afflitto da una serie di limiti. In particolare è lento e introduce un sovracalcolo di rilievo all’interno di Apache.

Installiamo e attiviamo PHP-FPM (FastCGI Process Manager), presente di serie in tutte le distribuzioni Linux. Si tratta di un miglioramento del vecchio del vecchio sistema CGI. Le elaborazioni PHP vengono portate fuori Apache e parallelizzate ai calcoli fatti da Apache. In questo modo si ottengono tre grandi benefici:

  • si accelera notevolmente l’elaborazione degli script PHP;

  • PHP viene eseguito in una bolla esterna al web-server Apache migliorando notevolmente la sicurezza;

  • come processo esterno possiamo assegnarlo ad un utente diverso dall’utente del web-server potendo creare tanti processi paralleli e indipendenti quanti sono i domini ospitati.

Si tratta del sistema default usato fa Nginx, l’altro grande e diffusissimo web-server.

Fatta questa premessa per capire cosa stiamo per fare e per dare ragione della scelta fatta da tutti per mettere in produzione un web-server LAMP passiamo a dare fare l’installazione e l’attivazione. Come abbiamo fatto nel manuale 1 operiamo sempre da terminale:

  • installiamo PHP-FPM

sudo apt install php8.1-fpm 
  • attiviamo i moduli e la configurazione di PHP-FPM

sudo a2enmod proxy_fcgi
sudo a2enconf php8.1-fpm
  • a questo punto abbiamo un nuovo file php.ini presente in /etc/php/8.1/fpm/php.ini . Aggiorniamo alcune direttive come fatto sopra

sudo nano /etc/php/8.1/fpm/php.ini


[ ... ]
memory_limit = 256M
[ ... ]
post_max_size = 64M
[ ... ]
sys_temp_dir = "/tmp"
[ ... ]
upload_max_filesize = 63MB
[ ... ]
date.timezone = Europe/Rome
[ ... ]
[apc]
;
; Stringhe non presenti da aggiungere alla fine di php.ini
; configurazione custom di APCu
;
apc.enabled=1
apc.shm_segments=1
apc.shm_size=32M
apc.entries_hint=4096
apc.ttl=1024
apc.gc_ttl=3600
apc.mmap_file_mask=
apc.slam_defense=1
apc.enable_cli=1
apc.use_request_time=1
apc.serializer=php
apc.coredump_unmap=0
; apc.preload_path=
  • a questo punto abbiamo due processi per il web-serve: apache2 ed il nuovo php8.1-fpm . Per attivare tutto dobbiamo riavviare entrambe i servizi

sudo systemctl restart php8.1-fpm apache2
  • verifichiamo il corretto caricamento e funzionamento di PHP-FPM aprendo il nostro web-browser e accedendo all’URL http://www.example.com/info.php ; troveremo diverse righe come la seguente /etc/php/8.1/fpm .

4.5. Composer

Composer non è propriamente una configurazione di PHP, ma un componente aggiuntivo.

Nel manuale 1 e nei paragrafi precedenti abbiamo spiegato che nel tempo PHP si è evoluto diventando progressivamente più complesso. Molto hanno contribuito i vari framework creati dai programmatori. Anche varie soluzioni di componenti esterni hanno contribuito ulteriormente a rendere più complesso il panorama. Va anche aggiunto, per completezza, che alcuni elementi funzionano correttamente solo se le altre parti sono di una certa versione.

Tutto questo ha contribuito alla creazione di un componente dedicato che usa una tecnologia consolidata nata in altri campi (in particolare Maven e Docker Composer). In pratica si crea un semplice file di testo dove ogni riga riporta un componente software da installare-prepara. In pratica un file descrittivo steso con una precisa sintassi. A questo punto sarà sufficiente, dalla riga di comando, invocare composer indicandogli quale file descrittore deve interpretare ed il software farà tutto il lavoro al posto nostro.

Alcuni software PHP, tra cui anche dei CMS, hanno iniziato a usare questa tecnologia per l’installazione. È il caso del potente e apprezzato CMS Drupal e di altri.

È opportuno essere informati dell’esistenza di questa tecnologia e di averla presente perché PHP Composer abitualmente non è nei repository e va aggiunto manualmente nel server.