PHP – 2 Array, operatori, costrutti 1 Array in PHP Gli array conservano un gruppo di valori che possiamo recuperare o attraverso un numero intero (iniziando dallo 0) oppure attraverso una stringa (array associativi) Esempio $vino['sa'] = "Furore Bianco"; $vino['av'] = "Greco di Tufo"; $vino['na'] = "Biancolella"; Tecnologie di Sviluppo per il WEB 2 Creazione Array Si può creare un array semplicemente inserendo degli elementi in esso $professori[0] = “costagliola”; $professori[1] = “blundo”; $professori[2] = “parente”; equivalente a: $professori[] = “costagliola”; $professori[] = “blundo”; $professori[] = “parente”; La funzione array() serve a creare un array. I parametri passati in input sono usati come elementi dell’array $professori = array(“costagliola”, “blundo”, “parente”); Tecnologie di Sviluppo per il WEB 3 Esempio $spesa = array("mele", "pere", "pane", "latte"); // oppure $spesa[0] = "mele"; $spesa[1] = "pere"; $spesa[2] = "pane"; $spesa[3] = "latte"; // ma anche $spesa[] = "mele"; $spesa[] = "pere"; $spesa[] = "pane"; $spesa[] = "latte"; Tecnologie di Sviluppo per il WEB 4 Accesso ad array La funzione count restituisce la grandezza dell’array Per accedere all’elemento i-esimo si accede all’elemento i-1 – $professori[2]; Se si accede ad un elemento non inizializzato non viene generato nessun errore Tecnologie di Sviluppo per il WEB 5 Esempio ESEMPIO <?php $professori[] = "blundo"; $professori[] = "costagliola"; $professori[2] = "parente"; $professori[10] = "d'ambrosio"; printf(“L'array contiene %d elementi\n”, count($professori)); print(“<br>”); for($i=0; $i< count($professori); $i++) { echo $i+1,") "; print(ucwords($professori[$i])); print("<br>"); } echo "Undicesimo elemento: ",$professori[10],"<br>"; echo "Centesimo elemento: ", $professori[99]; ?> Tecnologie di Sviluppo per il WEB 6 Risultato Tecnologie di Sviluppo per il WEB 7 Nota Invece di scrivere for($i=0; $i< count($professori); $i++) è meglio scrivere $cnt = count($professori); for($i=0; $i<$cnt; $i++) In questo modo la funzione count() è invocata solo una volta e non count($professori) volte Tecnologie di Sviluppo per il WEB 8 Altro modo per creare array Usando l’operatore => si possono specificare gli indici dell’array per ogni elemento, se non lo si usa l’array costruito è indicizzato sugli interi a partire dallo 0 (zero) $frutta = array(1=>”mele”, 2=>”pere”, 3=>”banane”); Tecnologie di Sviluppo per il WEB 9 Esempio <?php $frutta = array(1=>"mele", 2=>"pere", 3=>"banane"); $cnt=count($frutta); for($i=1; $i<= $cnt; $i++) echo $i, ") ", $frutta[$i], "<br>"; ?> Tecnologie di Sviluppo per il WEB 10 Esempio di array associativo $corso['blundo'] = “tsw1”; $corso['costagliola'] = “tsw2” $corso['parente'] = “tsw3”; Oppure $corso = array('blundo' => 'tsw1' , 'costagliola' => “tsw2” , 'parente' => 'tsw3' ); Tecnologie di Sviluppo per il WEB 11 Accesso ad array associativi Per far riferimento ad un elemento dell’array associativo usiamo la seguente sintassi: – $corso[parente] – $corso['costagliola'] – $corso["blundo"] – $corso[$nome] Tecnologie di Sviluppo per il WEB 12 Esempio <?php $corso = array('blundo' => 'tsw1', "costagliola" => 'tsw2', 'parente' => 'tsw3'); echo $corso[blundo],"<br>"; echo $corso['costagliola'],"<br>"; echo $corso["parente"],"<br>"; $nome= "blundo"; echo $corso[$nome]; ?> Tecnologie di Sviluppo per il WEB 13 Accesso alle chiavi (indici) di un array In PHP possiamo accedere anche al valore delle chiavi (indici) di un array associativo senza conoscerle. Si usa la funzione each($NomeArray) e le parole chiave key e value $corso e' il nome dell'array: for($i=0; $i< $cnt; $i++) { $riga = each($corso); echo $riga[key], " insegna ", $riga["value"],"<br>"; } Tecnologie di Sviluppo per il WEB ESMPIO 14 La funzione each() Ogni array in PHP conserva traccia del corrente elemento con cui stiamo lavorando – Il puntatore all’elemento corrente è noto come iteratore La funzione each() restituisce un array associativo che contiene due elementi e muove l’iteratore una posizione in avanti – Il primo elemento è indicizzato dalla stringa key – Il secondo elemento è indicizzato dalla stringa value Tecnologie di Sviluppo per il WEB 15 Altre funzioni su iteratori current() – Restituisce il valore dell’elemento puntato dall’iteratore corrente key() – Restituisce la chiave dell’elemento corrente reset() – Muove l’iteratore al primo elemento dell’array next() – prev() – end() Tecnologie di Sviluppo per il WEB 16 Esempio <h2>Utilizzo di iteratori su array</h2> <?php $corso = array(blundo => 'tsw1', "parente" => 'tsw2', 'cost' => 'tsw3'); reset($corso); echo "<table border=\"2\">\n"; echo "<tr> <th> Chiave </th> <th> Elemento </th> </tr>\n"; echo "<tr> <td> ", key($corso), "</td> <td>", current($corso), "</td> </tr>\n"; while(next($corso)) { echo "<tr> <td> ", key($corso), "</td> <td>", current($corso), "</td> </tr>\n"; } echo "</table>\n"; ?> Tecnologie di Sviluppo per il WEB ESEMPIO 17 HTML inviato al client <h2>Utilizzo di iteratori su array</h2> <table border="2"> <tr> <th> Chiave </th> <th> Elemento </th> </tr> <tr> <td> blundo</td> <td>tsw1</td> </tr> <tr> <td> parente</td> <td>tsw2</td> </tr> <tr> <td> costagliola</td> <td>tsw3</td> </tr> </table> Tecnologie di Sviluppo per il WEB 18 Ordinare array sort() – Ordina i valori dell’array (crescente) • distrugge il valore delle chiavi asort() – Ordina i valori dell’array conservando correlazione tra il valore e la sua chiave la ksort() – Ordina le chiavi conservando la correlazione tra il valore e la sua chiave rsort() – rasort() – rksort() – Ordinamento in ordine inverso ESEMPIO Tecnologie di Sviluppo per il WEB 19 Ordinare array secondo un proprio ordine usort(NomeArray, NomeFunzione) – ordina NomeArray secondo il criterio specificato dalla funzione NomeFunzione function cmp ($a, $b) { if ($a == $b) return 0; La funzione list return ($a < $b) ? -1 : 1; copia i valori } dell'array nelle vbls $arr = array (3, 2, 5, 6, 1); usort ($arr, "cmp"); while (list ($chiavve, $vallore) = each ($arr)) { Ordinamento echo "$chiavve: $vallore\n"; } crescente di $a Tecnologie di Sviluppo per il WEB 20 Risultato Il risultato dello script precedente è 0: 1 1: 2 2: 3 3: 5 4: 6 E' un esempio, sarebbe stato più opportuno usare la funzione sort() E' spesso usata per array multidimensionali. Tecnologie di Sviluppo per il WEB 21 Nota La funzione di confronto scritta dall’utente deve ricevere in input due parametri e restituire un intero – Minore di zero se il primo parametro è da considerarsi minore del secondo – Uguale a zero se i due parametri sono da considerarsi uguali – Maggiore di zero se il primo parametro è da considerarsi maggiore del secondo Tecnologie di Sviluppo per il WEB 22 Array multidimensionali – 1 Ogni elemento di un array può essere di tipo qualunque, anche di tipo array Un array multidimensionale è un array che contiene altri array for($i=1; $i < $n; $i++) for($j=1; $j < $n; $j++) $a[$i][$j] = $i * $j; Tecnologie di Sviluppo per il WEB 23 Array multidimensionali – 2 $riga0 = array(1, 2, 3); $riga1 = array(4, 5, 6); $riga2 = array(7, 8, 9); $multi = array($riga0, $riga1, $riga2); print_r($multi); /* human readable print */ Tecnologie di Sviluppo per il WEB 24 Ordinare array multipli con usort $products = array( array( 'Tires', 100 ), array( 'Oil', 10 ), array( 'Spark Plugs', 4 )); ordina sulla seconda colonna ESEMPIO function compare($x, $y){ if ( $x[1] == $y[1] ) return 0; else if ( $x[1] < $y[1] ) return -1; else return 1; } usort($products, 'compare'); Tecnologie di Sviluppo per il WEB 25 Funzioni su array (1/2) array_pad(array, nbr_elmnts, value) – Restituisce un nuovo array con nbr_elmnts, padded con altri elementi inizializzati allo stesso value. Esempi: – $scores = array(3,10); – $padded=array_pad($scores, 5,0); • $padded è (3,10,0,0,0); – $padded=array_pad($scores,-5,0); • $padded è (0,0,0,0,0) – $scores=array_pad($scores,5,0) • modifica “in situ” Tecnologie di Sviluppo per il WEB 26 Funzioni su array (2/2) array_chunk(array, size [,preserve_keys]) – Restituisce un array multidimensionale fatto degli elementi di array. Ogni riga è lunga size – preserve_keys: booleano array_slice(array, offset, length) – Restituisce un sotto-array di array lungo length a partire dalla posizione offset list($var1, $var2, …) = $array – Copia i valori di $array nelle variabili $var1, $var2 array_key_exists(chiave, array) – Restituisce un booleano se esiste la chiave nell'array. in_array(valore, array) – Booleano: se valore è in array Chunk Tecnologie di Sviluppo per il WEB 27 Esempio di list È usata per assegnare valori ad una lista di variabili in una sola istruzione, i valori sono presi da un array $info = array('caffè', 'scuro', 'caffeina'); // assegna tutte le variabili list($bevanda, $colore, $componente) = $info; // assegna solo in parte list($bevanda, , $componente) = $info; // assegna solo l'ultima variabile list( , , $componente) = $info; Tecnologie di Sviluppo per il WEB 28 Altre funzioni sugli array array_walk(array, func[,terzoPrmtr] ) – Chiama la funzione func su ogni elemento dell’array – func deve avere almeno due parametri (opzionalmente tre): il primo identifica il valore dell'elemento; il secondo identifica la chiave; il terzo un parametro generico di func. array_filter(array, func) – Restituisce un array. Ogni valore di array è passato a func, l’array restituito contiene solo quegli elementi di array per cui la funzione func ha restituito true Tecnologie di Sviluppo per il WEB 29 Esempio di array_walk <h2>Utilizzo di funzioni su array</h2> <?php $frutta = array ("uno"=>"limone", "due"=>"arancia", "tre"=>"banana"); function modifica (&$elemento, $chiave, $prefisso) { $elemento = "$prefisso:::::::::: $elemento"; } function stampa ($elemento, $chiave) { echo "$chiave: $elemento<br>\n"; } /* write */ /* read */ echo "<h3>Prima ...:\n</h3>"; array_walk ($frutta, 'stampa'); array_walk ($frutta, 'modifica', 'frutto'); echo "<h3>... e dopo:\n</h3>"; array_walk ($frutta, 'stampa'); ?> Tecnologie di Sviluppo per il WEB ESEMPIO 30 Esempio array_filter <h2>Utilizzo di array_filter</h2> <?php function dispari($var) { return ($var % 2); } function pari($var) { return (!($var % 2)); } $array1 = array ("a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5); $array2 = array (6, 7, 8, 9, 10, 11, 12); echo "<h3>Array iniziali :</h3>\n"; print_r($array1); echo "<br>"; print_r($array2); echo "<h3>Applico la funzione 'dispari' al primo array :</h3>\n"; print_r(array_filter($array1, "dispari")); echo "<h3>Applico la funzione 'pari' al secondo array:\n</h3>"; print_r(array_filter($array2, "pari")); ?> esempioWalkconFilter Tecnologie di Sviluppo per il WEB ESEMPIO 31 Conversione tra variabili e array PHP fornisce due funzioni extract() e compact() che permettono di creare variabili da array e viceversa – Si usano con gli array associativi $frutta = array ("uno"=>"limone", "due"=>"arancia", "tre"=>"banana"); extract($frutta); Dopo esistono le variabili $uno, $due e $tre, con i valori limone, arancia e banana, rispettivamente. Tecnologie di Sviluppo per il WEB 32 Nota su extract() Possiamo aggiungere automaticamente prefisso a tutte le variabili create un extract($NomeArray, EXTR_PREFIX_SAME, “prefisso”) Tutte le variabili che vanno in conflitto con una vbl esistente (con lo stesso nome) inizieranno con prefisso_ extract($frutta, EXTR_PREFIX_SAME, “gusto”); echo $gusto_uno; Tecnologie di Sviluppo per il WEB 33 compact() È il complemento di extract() – A partire da una lista di variabili, crea un array associativo le cui chiavi sono i nomi delle variabili $nome = "ambrogio"; $cognome = "unz"; $corso = "tsw"; $record = compact('nome', 'cognome', 'corso'); // oppure $chiavi=array(nome, cognome, corso); $record=compact($chiavi); Tecnologie di Sviluppo per il WEB 34 Array e stringhe In PHP esistono due funzioni per passare da una stringa ad un array e viceversa explode() – Passa da una stringa ad un array implode() – Passa da un array ad una stringa Tecnologie di Sviluppo per il WEB 35 explode() Applicata su una stringa restituisce un array di sottostringhe individuate dal delimitatore Sintassi explode(delimitatore, stringa [, limite]) • limite è un parametro opzionale che indica il massimo numero di elementi da estrarre da stringa Esempio $pizza = "mozzarella pomodoro basilico olio"; $ingredienti = explode(" ", $pizza); Tecnologie di Sviluppo per il WEB 36 implode() Applicata ad un array restituisce una stringa rappresentante gli elementi dell’array divisi da un separatore Sintassi implode(separatore, array) Esempio $array = array('ambrogio', '[email protected]'); $stringa = implode(",", $array); ESEMPIO Tecnologie di Sviluppo per il WEB 37 Oggetti in PHP PHP supporta la orientata agli oggetti PHP supporta programmazione – Incapsulazione – Polimorfismo – Ereditarietà L’ereditarietà multipla è supportata come in JAVA (con le interfacce) Vedremo un esempio di OOP con una classe per MySQL (?) Tecnologie di Sviluppo per il WEB 38 PHP: 5 – 4 Membri (1) e metodi – public – private – Protected Metodi e Classi – Astratte • Solo firme, senza implementazioni Interfacce Tecnologie di Sviluppo per il WEB 39 PHP: 5 – 4 La (2) parola chiave per metodi e membri – final Clonare Oggetti – function __clone() • Viene verificato se esiste un tale metodo nella classe. In caso negativo una versione di default viene invocata. Tecnologie di Sviluppo per il WEB 40 PHP: 5 – 4 Costruttore (3) Unificati – function __construct() • È utile per invocare costruttori di genitori da classi derivate – Non c'è bisogno di conoscere il nome Distruttori – function __destruct() • Quando l'ultimo riferimento ad un oggetto è distrutto, il metodo distruttore viene invocato. • Non ha parametri Tecnologie di Sviluppo per il WEB 41 PHP: 5 – 4 (4) Costanti – const cstn= “HELLO WORLD”; Eccezioni – C'è il supporto per la clausola • catch all – Ma non per quella • finally Vbls membri static di Classi Statiche possono ora essere inizializzate Metodi statici Tecnologie di Sviluppo per il WEB 42 PHP: 5 – 4 (5) Metodo – Instanceof • Utile per accertarsi se un oggetto è una istanza di una classe, estende una classe oppure implementa un'interfaccia Iterazione <?php class Foo { var $x = 1; var $y = 2; } $obj = new Foo; foreach ($obj as $prp_name => $prop_value) { echo “$prp_name = $prop_value\n”; } ?> Tecnologie di Sviluppo per il WEB 43 PHP: 5 – 4 Il (6) nuovo metodo magico – __toString() • Permette di sovraccaricare la conversione a stringa dell'oggetto Altri dettagli all'URL – http://www.zend.com/php5/articles/engine2-php5-changes.php Tecnologie di Sviluppo per il WEB 44 Operatori in PHP – 1 Aritmetici +, -, *, /, % Assegnamento = (supporta la forma abbreviata: e.g., += ) Incremento/decremento ++ e -- (prefisso/postfisso) Operatori bitwise &, |, ^ (xor), ~, <<, >> Tecnologie di Sviluppo per il WEB 45 Operatori in PHP – 2 Confronto ==, ===(tipato), !=, !==, <, >, <=, >= Logici &&, and, ||, or, xor, ! Condizionale ?: (if aritmetico) Casting (int), (float), (string), (bool), (array), (object) Tecnologie di Sviluppo per il WEB 46 Operatori in PHP – 3 Controllo dell’errore (@) – Quando @ precede un comando, gli errori eventualmente generati sono ignorati Esecuzione (`) – La stringa all’interno di ` (backtick) è passata alla shell per essere eseguita: $output = `ls -al`; echo "<pre>$output</pre>"; – domanda: se tolgo <pre>?? backtick.php Tecnologie di Sviluppo per il WEB 47 Operatori di confronto L’operatore == testa se due espressioni sono uguali (anche dopo aver effettuato delle conversioni di tipo) • “1” == “1” -> true • “1” == 1 -> true L’operatore === testa se due espressioni sono identiche (le due espressioni devono essere uguali e dello stesso tipo) • “1” === “1” -> true • “1” === 1 -> false ESEMPIO Tecnologie di Sviluppo per il WEB 48 Istruzioni per il controllo del flusso – 1 if (condizione) istruzioni if(condizione) istruzioni1 else istruzioni2 Tecnologie di Sviluppo per il WEB 49 Istruzioni per il controllo del flusso – 2 if(condizione1) istruzioni1 elseif(condizione2) istruzioni2 …. else istruzioniN Tecnologie di Sviluppo per il WEB 50 Istruzioni per il controllo del flusso – 3 Istruzione switch switch($a) { case 1: print("a è uguale a uno"); break; case 2: print("a è uguale a due"); break; default: print("a non vale né uno né due"); } Tecnologie di Sviluppo per il WEB 51 Cicli while(condizione) istruzioni do istruzioni while(condizione); for(inizializzazione; condizione; incremento) istruzioni Tecnologie di Sviluppo per il WEB 52 Ciclo foreach Permettere di iterare sugli elementi di un array foreach($nomeArray as $elemento) { istruzioni } $elemento assumerà tutti i valori contenuti in $nomeArray Tecnologie di Sviluppo per il WEB 53 Ciclo foreach – 2 Possiamo scorrere contemporaneamente le chiavi e gli elementi di un array foreach($nomeArray as $indice => $elemento) { istruzioni } Le variabili $indice ed $elemento assumeranno, rispettivamente, tutti i valori delle chiavi e degli elementi contenuti in $nomeArray Tecnologie di Sviluppo per il WEB 54 exit Termina l’esecuzione di uno script Ha un argomento opzionale – Se è un numero, indica lo stato di uscita del processo – Se è una stringa, viene stampata prima che il processo termini – exit() è un sinonimo di die() $handle = @mysql_connect(“localhost”,$user, $passwd) or die(“Connessione fallita”); Tecnologie di Sviluppo per il WEB 55 Includere del codice – 1 PHP offre due possibilità per includere un documento (e.g., una libreria) in uno script PHP include e require La differenza principale è che require genera un errore fatale se non si trova il file da includere; mentre, include genera solo un warning Sintassi – include ‘NomeFile’; require ‘NomeFile’ Tecnologie di Sviluppo per il WEB 56 Includere del codice – 2 Per sopprimere gli errori possiamo usare @include In genere require è usato per includere delle librerie; mentre, include è usato per includere del codice HTML comune a più documenti Inclusioni di file con estensione .php includono l’output dello script – quindi... Tecnologie di Sviluppo per il WEB 57 Includere del codice – 3 Per cui si usa l’estensione .inc per le librerie e l’estensione .html per documenti Un modo più efficiente per gestire intestazioni e piè pagina è quello di includere una libreria e poi invocare delle funzioni per generare le parti comuni Se vogliamo essere sicuri che il file sia inserito una sola volta usiamo require_once e include_once Tecnologie di Sviluppo per il WEB 58 Esempio – pagina.php <html> <head> ….. </head> <body> ESEMPIO <?php include ‘header.html’; ?> Contenuto HTML <?php include ‘footer.html’; ?> </body> </html> Potrebbe andare in un file che viene incluso Tecnologie di Sviluppo per il WEB 59 Esempio di Libreria <html> <head> ….. </head> <body> <?php require_once ‘sito.inc’; genHeader(); ?> Contenuto HTML <?php genFooter(); ?> </body> </html> Tecnologie di Sviluppo per il WEB 60 Funzioni in PHP – 1 La sintassi per definire una funzione in PHP è la seguente: function NomeFunzione([lista parametri]) { implementazione } La lista dei parametri è una sequenza di variabili separate da virgola – – – – Non è permesso l’overloading Il nome delle funzioni NON è case sensitive È possibile usare un numero variabile di argomenti Inoltre ad ogni parametro può essere assegnato un valore di default...: att.ne (vedi slides successive) Tecnologie di Sviluppo per il WEB 61 Valori di default <?php // il terzo parametro ha un valore di default function descrivi_film($titolo, $regista, $bn_colori = 'colori') { echo "Titolo: $titolo <br>"; echo "Regista: $regista <br>"; echo "B/N - colori: $bn_colori <br>"; } // chiamo la funzione passando 2 soli parametri // il terzo è sottointeso descrivi_film('Il malato immaginario','Tonino Cervi'); echo '<br>'; // chiamo la funzione passando tutti e 3 i parametri descrivi_film('I soliti ignoti','Mario Monicelli','bianco e nero'); ?> Tecnologie di Sviluppo per il WEB 62 Scope di vlbs: (meravigliati?) 1. <?php 2. function quadrato($a) 3. { 4. $a = $a * $a; 5. } 6. 7. $numero = 2; 8. quadrato($numero); 9. echo $numero; //stampa 2 !!! 10. ?> Tecnologie di Sviluppo per il WEB 63 Funzioni in PHP – 2 Gli argomenti possono essere passati per valore o per riferimento Per passare un argomento per riferimento è sufficiente, nella definizione della funzione, far precedere l’argomento da & – La chiamata si effettua come al solito Se il nome della funzione è preceduto da &, allora la funzione restituirà un valore per riferimento Tecnologie di Sviluppo per il WEB 64 Funzioni in PHP – 3 Le variabili all’interno di una funzione sono sempre locali Per accedere alle variabili globali dobbiamo dichiararle con la parola chiave global Le funzioni non conservano memoria della loro esecuzione a meno dell’uso di variabili static Una funzione definita in uno script è disponibile ovunque in quello script Per chiarezza è meglio definire le funzioni all’inizio, ma non è necessario Tecnologie di Sviluppo per il WEB 65 Funzioni in PHP – 4 In aggiunta alle usuali funzioni fornite dai linguaggi di programmazione (manipolazione di array, date e tempo, stringhe, funzioni matematiche, …), PHP supporta un grande numero di funzioni che lo collegano ad altri tipo di software (molte di queste funzioni richiedono una speciale installazione): – – – – – MySQL, MS SQL, Oracle 8 Shockwave Flash PDF, XML IMAP, POP3, NNTP E tanti altri ancora… Tecnologie di Sviluppo per il WEB 66 Funzioni con numero variabile di argomenti Esistono tre funzioni per accedere al numero dei parametri ed ai parametri stessi di una funzione da 0 a – $array = – $num = – $valore = func_get_args(); $num-1 func_num_args(); func_get_arg(indice); Il risultato di uno qualsiasi di queste funzioni non può essere usato direttamente, ma deve sempre essere associato prima ad una variabile Tecnologie di Sviluppo per il WEB 67 Valori restituiti Passati con l’istruzione return Per far restituire più valori è sufficiente restituire un array function return_due() { return array(“a”, 3); } Tecnologie di Sviluppo per il WEB 68 Uso di funzioni anonime (closure), come assegnazioni a variabili. È possibile associare il nome di una funzione ad una variabile e poi invocare la funzione attraverso questa variabile function max($a, $b) { return ($a>$b ? $a : $b); } $massimo = “max”; $massimo(3,4); //restituisce 4 Tecnologie di Sviluppo per il WEB 69 Ancora funzioni anonime Possiamo crearle con – create_function(); Sintassi $valoreRestituito = create_function($stringaArgomenti, $stringaCorpo); Esempio $lambda = create_function(‘$a, $b’, ‘return(strlen($a) – strlen($b));’ ); Tecnologie di Sviluppo per il WEB 70
© Copyright 2024 Paperzz