Come inserire un pupup di alert o conferma in una pagina HTML o PHP per eseguire un comando javascript o un comando derivato da una chiamata javascript? Vediamo i codici.
E questo e’ il risultato
Cliccando sul bottone “CHIEDI CONFERMA” apparira’ il popup sopra con la possibilita’ di premere solamente OK e far procedere lo script della funzione chiediconferma().
confirm()
il confirm invece da’ la possibilita’ di dare o meno la conferma al comando, vediamo il codice
che produrra’
Il codice javascript fara comparire il popup e chiedera’ se confermare o meno l’azione, se l’utente preme su OK la condizione dell’ if(test) sara’ vera ed eseguira’ il codice conseguente , altrimenti verra’ eseguito il codice compreso nell‘else.
prompt()
Con questo metodo, oltre che hai due pulsanti Annulla e OK sara’ presente anche un campo editabile dove inserire una stringa.
A questo punto oltre che a verificare l’avvenuta pressione del tasto OKl’if verifichera’ che il nome inserito sia “Mario” , se fosse qualsiasi altro nome o venisse premuto il tasto Annulla il codice eseguito sara’ quello dell’ else
Le comprehension (comprensione) e’ una forma di scrittura del codice, che serve ad ottimizare la generazione di liste, set e dizionari la sua sintassi e’ la seguente
per le liste:
lista = [ <espressione sulla variabile> for <variabile> in <input_> <filtro opzionale> ]
per le set:
lista = { <espressione sulla variabile> for <variabile> in <input_> <filtro opzionale> }
per i dictionary:
dic = { <espressione(variabile Key): espressione(variabile Value)> for variabile Key,variabileValue in <input_key, input_value> <filtro opzionale> }
Dettagli dei parametri:
<espressione sulla variabile> e’ un esperssione matematica o una funzione che agisce sul valore della variabile ricavato dal <input_> il quale puo essere una qualsiasi struttura dati o funzione
<input_> struttura o funzione (es. range()) che fa da base dati per la generazione della nuova struttura dati
<filtro opzionale> e’ una condizione if che se rispettata scrvie il dato nella nuova struttura, altrimenta la scarta.
Vediamo alcuni esempi :
#genero una set di divisi per due
list1 = [12,13,14,15,16,17,18]
set2 = {x/2 for x in list1}
print(set2)
#genero una lista di divisi per due sono se interi
list2 = [12,13,14,15,16,17,18]
set3 = [x/2 for x in list2 if x%2 == 0]
print(set3)
#genero un dizionario delle temperature massime per mese
mesi = [('GENNAIO',-1),('FEBBRAIO',-2),('MARZO', 4),('APRILE',9)]
temp_mesi = { m: t for m,t in mesi }
print(temp_mesi)
#genero un dizionario dei mei con temperature massime sotto 0
temp_mesi_freddi = { m: t for m,t in mesi if t < 0}
print(temp_mesi_freddi)
######risultati
{6.0, 7.0, 8.0, 7.5, 6.5, 8.5, 9.0}
[6.0, 7.0, 8.0, 9.0]
{'GENNAIO': -1, 'FEBBRAIO': -2, 'MARZO': 4, 'APRILE': 9}
{'GENNAIO': -1, 'FEBBRAIO': -2}
Sono Iteratori creati ad-hoc dal programmatore, praticamente sono delle CLASSI che implementano almeno i due metodi __iter__() e __next__()
nel metodo __iter__() Avviene l’inizializzazione dell’iteratore e il suo return (di solito viene ritornato l’oggetto stesso)
nel metodo __next__() ritorna il prossimo elemento nella sequenza e solleva l’eccezione StopIteration alla fine degli elementi
Facciamo un esempio di un iteratore che ad ogni chiamata restituise il valore precedente aumentato di 1.44 partendo da 0
class numeri:
#inizializzo l'iteratore partendo da 0
def __iter__(self):
self.x = 0
return self
#calcolo valore successivo restituito ad ogni chimata next(<oggetto>)
def __next__(self):
a = self.x
self.x += 1.44
return round(a,2)
oggetto = numeri()
#converto in iteratore
test = iter(oggetto)
#scorro iteratore
for i in range(0,100):
print(next(test))
I commenti al codice dicono tutto, l’unica cosa e’ che questo iteratore non finira’ mai di generare in quanto nella dichiarazione del metodo __next__() non c’e’ limite, pertanto lanciando un for i in range(oggetto) il ciclo durerebbe all’infinito. Se ad esempio volessimo fermare la generazione al numero superiore a 144.00 dovremmo modificare il codice nel modo seguente:
class numeri:
def __iter__(self):
self.x=0
return self
def __next__(self):
a = self.x
self.x +=1.44
if(a <= 144):
return round(a,2)
else:
StopIteration
oggetto = numeri()
test = iter(oggetto)
for i in range (0,120):
print(next(test))
In fine vediamo un esempio di iteratore customizzato con la possibilita’ di passergli dei parametri (esempio range(0,100):
class numeriDispari(object):
def __init__(self, max):
self.max = max #imposto il valore massimo
self.n = -1 #imposto la partenza che sara sempre 1
def __next__(self):
self.n += 2 #incremeto di 2 il valore precedente ( all inizio self.n = -1, quindi self.n + 2 = 1)
if self.n > self.max:
raise StopIteration #se supero il valore max fermo l'iterazione
else:
return self.n #altrimenti rimando il valore calcolato
def __iter__(self):
return self
#stampo i primi 10 numeri dipsari
numeri = numeriDispari(10)
for n in numeri:
print(n)
#creo una lista dei primi 80 numeri dipari
numeri_d = [ x for x in numeriDispari(100) if x < 80]
print(numeri_d)
In python i concetti iterabile e iteratori possono essere cosi descritti:
Iterabile: Oggetto o struttura dati (liste,touple etc etc) che puo essere trasformato in un iteratore, un altro esempio di iterabile sono le stringhe. l’oggetto iterabile rimane in memoria e puo’ essere usato in qualsiasi momento durante l’uso nel codice
Iteratore: Oggetto o struttura dati che puo’ essere ‘attraversato, scandito’ con i metodi __iter()__ e __next()__ e “ricoda” il suo stato mentre si attraversa. L’oggetto iteratore, una volta “attraversato” viene eliminato e quindi non e’ possibile riutilizzarlo o scorrerlo avanti e indietro. In generale un iteratore non genera i dati che ha dentro di se ma tiene traccia della loro posizione nella fonte da cui li preleva
Facciamo un esempio con una stringa che e’ un oggetto iterabile:
Come vediamo la prima print stampa correttamente la stringa mentre la seconda stampera’ la locazione di memoria dell’oggetto iterabile. per stampare il contenuto dell’oggetto str_iterabile dovremmo usare il metodo next()
Quali sono i principali vantaggi nell’uso di un iteratore?
il principale vantaggio sta’ nel occupare poca memoria nel trattare molti dati, ad esempio la lettura di un file CSV tramite un iteratore legge ed elabora una riga alla volta per poi passare alla successiva ‘dimenticandosi’ dei dati precedenti
I generatori sono delle speciali funzioni che restituiscono un iterabile, un esempio di generatore e’ la funzione range(x,y,z) usata per generare una sequenza di interi che parte da x fino ad y con passo z.
ad esempio la chiamata range(0,10,1) assegnata al comando print mi crea una lista di valori da 0 a 10 con passo 1
x = range(0,10,1)
print(list(x)
Questo codice restituira una lista [0,1,2,3,4,5,6,7,8,9]
Creiamo adesso un generatore che restituisca una lista di valori calcolati
def generatore(x):
for y in range(0,x)
y = y - 1
yield y
test = generatore(4)
print(list(test))
Questo codice resittuira’ una lista [-1,0,1,2]
Vediamo di capire come funziona. Alla prima chiamata di test = generatore(4) entro nella funzione generatore(x), la funziona mi genera un elenco di naturali che partono da 0 a 4, al primo ciclo y vale 0 -1 = -1, al secondo giro di for y = 1 -1 = 0 e cosi’ via.
Scrittura dei generatori in modalita’ COMPREHENSION
La modalita’ comprehension (compressa) e’ una modalita di scrittura in python molto utilizzata sia nei generatori che nelle creazione di strutture dati tipo liste, touple dizionari etc etc. vediamo ora come scrivere lo stesso generatore di prima in modalita’ comprehension:
test= ( y-1 for y in range(10))
print(list(test))
Queste due righe daranno come risultato lo stesso di quello precedente!
Liste, touple set e dizionari in python sono delle strutture dati iterabili, anche non omogenee, indicizzate o meno che facilitano la gestione dei dati stessi, potendoli organizzare, ordinare o combinare assieme.
Vediamone le differenze principali :
nome
sintassi
descrizione
esempi
liste
lista = [x,y,z ….]
Sono strutture molto simili agli array in altri linguaggi di programmazione, sono indicizzate partendo da 0 e possono essere modificate in seguito alla loro creazione
l1 = [1,2,’ciao’,3.15]
touple
tuple = (x,y,z …)
Sono come le liste ma non sono modificabili in seguito alla loro creazione, ne nella dimensione ne nei loro contenuto, nascono e muoiono cosi come sono
t=(1,4,5.4,”ciao”)
set
set = {x,y,z…}
I set sono come le liste, possono essere modificate nella dimensione e nel contenuto manon accettano valori uguali
s = {1,2,3,’c’,”radio”}
dizionari
dic = {key: val, key: val … }
Sono delle strutture non indicizzate, contengono delle coppie chiave:valore sono modificabili ed eterogenee la chiave puo essere numerica o alfanumerica cosi’ come i valori.
d = { uno: “primo”, due: 2, 3 : 3,15 }
Ogni struttura puo contenere al suo interno altre strutture ad esempio una tupla contenuta in una lista :
lista = [1,’c’,(1,3,4,5)]
per accedere ai dati della tupla si scrivera’ : dato = lista[2][0] in questo caso dato conterra’ il valore 1. Chiaramente non potro modificare il valore lista[2][0] essendo una tupla, ma potro modificare il valore lista[0] o lista[1] essendo una lista.
dizionario = { 1: ‘c’, 2: (2,1) }
anche in questop caso potro accedere senza modificare ai valori della toupla nel seguente modo : dato = dizionario[2][1] la variabile dato conterra’ il valore 1
Indirizzare l’output dei comandi shell in un file.
Cosa potrebbe serivre questa funzionalita’? Potrebbe essere necessario monitorare uno script controllandone l’esecuzione o gli errori, oppure potrebbe essere utile per indirizzare l’output del comando in un file su cui un’altro script recuperera’ le informazioni per eseguire altre funzioni.
Vediamo ora i diversi metodi per indirizzare l’output in un file
user@linux:$ comando > nomefile.txt
Questa modalita crea all’esecuzione di ‘comando’ un file di nome ‘nomefile.txt’ con all’interno tutto quello che sarebbe stato stampato a video nella shell di esecuzione, se il file non esiste lo crea, altrimenti se gia’ esistente lo sovrascrive. Dunque il simbolo ‘>’ sta a indicare la modalita di scrittura nel file. Vediamo quali sono le opsioni che possiamo usare :
simbolo
descrizione
>
Scrive in nuovo file, se esiste lo sovrascrive
>>
Scrive accodando alla fine del file se esiste, se non presente crea il file
2>
Scrive solo gli errori standard se file esiste lo sovrascrive. L’output del comando viene mandato a video
2>>
Come sopra ma accoda gli errori alla fine del file
&>
Scrive sia l’output che gli errori standard sul file e non visualizza nulla sul video
&>>
come sopra ma accoda alla fine del file
Un secondo metodo per reindirizzare e’ l’uso di tee. Se con il metodo precedente l’output del comando veniva reindirizzato sul file senza mostrare nulla a monitor, con tee e’ possibile indirizzare l’output nel file ed allo stesso tempo visualizzarlo sul monitor.
user@linux:$ comando | tee nomefile.txt
Nella tabella sotto sono riportati le varie versioni del comando e il loro risultato.
metodo
descrizione
| tee
manda a schermo il risultato del comando in piu scrive sul file, se il file esiste lo sovrascrive
| tee -a
manda a schermo il risultato del comando in piu scrive sul file, se il file esiste aggiunge il testo alla fine
|& tee
Manda a schermo sia l’output che gli errori standard in piu scrive tutto sul file , se esiste lo sovrascrive
|& tee -a
Manda a schermo sia l’output che gli errori standard in piu scrive tutto sul file se esiste aggiunge il testo alla fine
Con l’ausilio dell’ istruzione exec() e’ possibile far eseguire dei comandi o degli script nella shell del server in cui sta girando PHP. L’utente con cui verranno eseguiti i comandi o gli script sara’ chiaramente l’utente web di default cioe’ wwwdata, pertanto i permessi di esecuzione lettura e scrittura sul disco saranno giustamente limitati. Siccome il PHP e’ un linguaggio interpretato che lavora lato server e’ chiaro che tutti i comandi lanciati tramite la pagina web, eseguiranno le operazioni lato server e non lato client. vediamo un semplice esempio di come funziona il comando.
<?php
exec("ls", $output, $result);
?>
Il codice sopra non ritornera’ nulla sulla pagina eseguita! infatti i dati in uscita dal comando ‘ls’ saranno memorizzati nel array $output, mentre nella variabile $result sara’ contenuto 1 se il comando ha restituito un errore o non ha restituito nulla, 0 se il comando e’ stato eseguito correttamente.
Andiamo ora a stampare a video il risultato del comando ls:
Possiamo vedere i due file presenti nella directory di esecuzione del comando, se volessimo sapere quali file sono presenti nella directory radice, e’ sufficiente sostituire “ls” con “ls ../../”
Conoscendo la potenza della shell e dei sui script e’ possibile fare qualsiasi cosa sul server, direttamente non puo’ creare, modificare o cancellare file ma attraverso degli appositi script e’ possibile fare molti danni, come la cancellazione di file o di intere directory!
Tabella comandi shell di linux comparati per funzione
Con le nuove distribuzioni di linux alcuni comandi sono cambiati. Nella tabella successiva verdiamo i comandi piu’ utilizzati e i loro nuovi corrispondenti
Analizziamo il caso in cui il client attraverso la rete internet, voglia accedere alla pagina web che risponde alla porta 8080 sul SERVER A con ip privato 192.168.0.1. Di questa pagina abbiamo una copia perfetta sul SERVER B con ip privato 192.168.0.2 che risponde sulla porta 80.
In condizioni normali, quindi, il client aprira’ il suo browser ed inserira’ nella barra degli indirizzi http://10.1.1.1:8080 corrispondente all’ip pubblico del router. La richiesta arriva al router che tramite le sue regole di nat indirizzera’ la richiesta al SERVER A il quale rispondera’ con la pagina web contenuta nel suo disco rigido.
Oggi pero’ abbiamo deciso di eseguire una manutenzione al WEB del SERVER A e chiaramente non possiamo permetterci che i CLIENT non abbia il servizio se richiesto. Non abbiamo accesso al firewall per modificare gli instradamenti e pertanto dobbiamo escogitare un trucco in modo che ad ogni richiesta della pagina WEB fatta al SERVER A sulla porta 8080, risponda il SERVER B sulla 80.
SOLUZIONE.
Sulle macchine linux ci viene in aiuto IPTABLES. IPTABLES e’ l’utility a riga di comando che ci permette di modificare le regole di firewall e di instadamento sui pacchetti che arrivano al o partono dal server.
Non faro’ qui un trattato su cosa sia un firewall e come lavori in quanto ci vorrebbero pagine e pagine di articoli ma mi limito brutalmente a dire che il firewall e’ quella parte di software che decide del destino dei pacchetti che nascono o che arrivano alla porta ethernet del server(detta veramente in modo brutale).
Vediamo ora cosa dobbiamo fare fisicamente sulla consolle della macchina SERVER A in modo che ad ogni richiesta che gli arriva sulla sua porta ethernet sulla porta 8080 giri la richiesta alla porta 80 del SERVER B.
Innanzi tutto per questo esempio ho usato due SERVER con installato DEBIAN 11, ma il concetto rimane valido per tutti i sistemi LINUX, prima operazione da fare e’ verificare che sia attiva la funzione FORWARDING del KERNEL mediante il seguente comando dal terminale.
root@SERVER_A:cat /proc/sys/net/ipv4/ip_forward
dovrebbe rispondere semplicemente con un 1. Nel caso la risposta sia 0 , sara’ necessario abilitare il forwarding e riavviare il servizio di rete con i seguenti comandi
root@SERVER_A: echo 1 > /proc/sys/net/ipv4/ip_forward
root@SERVER_A: systemctrl restart network
per le ultime verisioni di UBUNTU e DEBIAN
root@SERVER_A: systemctrl restart NetworkManager
Fatto questo dobbiamo inserire la regola nel firewall del SERVER_A tramite IPTABLES
Verifichiamo che la regola sia stata inserita con il seguente comando
root@SERVER_A: iptables -t nat -L --line-numbers
Rispondera con una serie di regole tra cui troverete le seguenti righe
Chain PREROUTING (policy ACCEPT)
num target prot opt source destination
1 DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:192.168.0.2
A questo punto abbiamo finito, il client richiedera’ quindi la pagina web sempre all’indirizzo:porta pubblico http://10.1.1.1:8080 il router/firewall indirizzera’ la richesta sempre al SERVER A che ora pero’ invece di rispondere con il contenuto della pagina, rimandera’ la richiesta al SERVER B sulla porta 80 il quale mandera’ il contenuto della pagina WEB al client che l’ha richiesta.
Cosa dobbiamo fare invece se volessimo cancellare la regola e ripristinare la situazione iniziale? e sufficiente inserire il seguente comando
root@SERVER_A: iptables -t nat -D PREROUTING 1
dove 1 sta' per il numero di regola(num)
Ora il SERVER A riprendera’ a rispondere direttamente alle richieste sulla porta 8080.