Formularis i fitxers
Formularis
Pas de paràmetres
Una petició http és la sol·licitud d’un recurs al servidor:
- Es realitzen a través d’una url
- Es poden passar paràmetres amb la petició
- Hi ha diferents mètodes (METHOD) de realitzar una petició (GET, POST, PUT, DELETE, PATCH, etc.), encara que els més habituals són GET i POST
Petició GET
- S’utilitza per a sol·licitar dades d’un recurs
- Es Mostren els paràmetres que s’envien en la url
- Es poden utilitzar directament en enllaços
- Romanen en l’historial del navegador
- La grandària dels paràmetres està limitat a 255 caràcters
Accedir a les dades de la petició GET
- Usem la variable superglobal $_GET
- És un array associatiu
- Les claus del array coincidiran amb els noms que li hem donat als paràmetres.
- Per a accedir als paràmetres de la petició anterior:
echo $_GET['nom'] . ' ' . $_GET['cognom'];
Evitar el CSRF
Video](https://youtu.be/qywV3Im17kk)
En tota pàgina que reba paràmetres GET has de comprovar el HTTP referer del navegador, i que aquest siga de dins de la teua web. En php el referer que envia el navegador s’emmagatzema en $_*SERVER[‘HTTP_REFERER’]
Seria tal com:
if( parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) != $_SERVER['HTTP_HOST']) die('Anti-CSRF');
NOTA IMPORTANT Amb aquest codi estem obligant al fet que el navegador envie un referer si o sí. Per tant només ha d’utilitzar-se en pàgines a les quals el navegador accedisca des d’una altra pàgina de la nostra web. Òbviament no podem col·locar-ho en la primera pàgina a la qual s’accedeix a la nostra web (index.php o similar), ja que si l’usuari a escrit l’adreça a mà en la barra del navegador no s’enviarà referer cap i saltarà el sistema.
Petició POST
- S’utilitza per a enviar dades a un recurs
- Els paràmetres van en el cos de la petició, no són visibles per a l’usuari
- No es pot utilitzar en un enllaç
- No roman en l’historial
- Se solen utilitzar en els formularis
- No tenim la limitació de grandària dels paràmetres
Accedir a les dades de la petició POST
- Usem la variable superglobal $_POST
- Funciona igual que $_GET.
- Mostrar totes les dades rebudes:
var_dump($_POST);
- Mostrar les dades individualment:
echo $_POST['nom']; echo $_POST['cognom'];
Al crear un formulari li assignem un metode (habitualment POST) i una destinació (pàgina que tractarà el formulari)
<form method='POST' action='pagina.php'>
Enviar les dades al mateix script que mostra el formulari
És habitual que la pàgina que tracta el formulari siga la mateixa que la que el conté. Per poder fer-ho:
- La variable $_SERVER conté dades relacionades amb l’entorn del servidor de la petició HTTP
- Una de les dades que conté és el script php que s’està executant ($_SERVER[‘PHP_SELF’]). Si indiquem el action del formulari així: action=”<?= $_SERVER[‘PHP_SELF’]; ?>”, serà la pròpia pàgina del formulari la que processe les dades.
- Si és així haurem de programar dos blocs:
- Un inicial de comprovació que s’executa quan s’accedix pel mètode POST,es a dir, a l’enviar el formulari. Si les dades són correctes es processarà el formulari. En cas contrari es crea la variable $err i continua l’script.
- L’HTML, que s’executa quan s’accedix mitjançant GET (no ve del formulari) o després d’executar el primer bloc, si així ho volem.
- Un inicial de comprovació que s’executa quan s’accedix pel mètode POST,es a dir, a l’enviar el formulari. Si les dades són correctes es processarà el formulari. En cas contrari es crea la variable $err i continua l’script.
Verificar que el formulari s’ha enviat
- Abans de mostrar les dades verificarem que s’haja enviat el formulari:
<?php
/* si va bien redirige a principal.php si va mal, mensaje de error */
if ($_SERVER["REQUEST_METHOD"] == "POST") {
if($_POST['usuario'] === "usuario" and $_POST["clave"] === "1234"){ header("Location: principal.php");
}else{
$err = true;
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Formulario de login</title>
<meta charset = "UTF-8">
</head>
<body>
<?php if(isset($err)){
echo "<p> Revise usuario y contraseña</p>";
}?>
<form action = "<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method = "POST">
<label for = "usuario">Usuario</label>
<input value = "<?php if(isset($usuario))echo $usuario;?>"
id = "usuario" name = "usuario" type = "text">
<label for = "clave">Clave</label>
<input id = "clave" name = "clave" type = "password">
<input type = "submit">
</form>
</body>
</html>
Accedir a paràmetres no existents
Validació del formulari
- Hem de comprovar que les dades del formulari són correctes
- Validacions a realitzar:
- Els camps requerits no han de quedar buits
- Els camps email i data han de tenir el format esperat
- Tots els camps s’han de filtrar.
Valors buits
- Els camps requerits no haurien de quedar-se buits.
- Per a verificar que un valor no queda buit podem utilitzar la funció empty() de PHP. http://php.net/manual/es/function.empty.php .
Espais en blanc
- Hem d’eliminar els espais en blanc del principi i final dels camps
- S’utilitza la funció trim()
Filtrar l’entrada
- Sempre hem de filtrar l’entrada amb htmlspecialchars() abans de mostrar el camp amb echo o similar. Si mostrem l’entrada tal cual podem patir atacs XSS o d’injecció.
Comprovar l’email
- Per a verificar si un email és correcte podem utilitzar la funció filter_var($email, FILTER_VALIDATE_EMAIL)) http://php.net/manual/es/function.filter-var.php
Dates
- En PHP les dates s’emmagatzemen com a números.
- S’utilitza la classe DateTime per a representar-les
- Igual que ocorre amb les cadenes no té sentit estudiar les funcions relacionades una a una. Les pots consultar en: http://php.net/manual/es/ref.datetime.php
- Per a comprovar la data devem crear una funció a aquest efecte
- Podemos utilitzar el mètode createFromFormat() de la classe DateTime http://php.net/manual/es/datetime.createfromformat.php
Mostrar els errors en el formulari
El errors de validació s’han de guardar en una variable per a mostrar-los a l’usuari junt al formulari per tal que torne a picar-los bé. També he de guardar els valors anteriors per mostrar-los també.
function isRequired($nomCamp,&$errors){
if (!empty($_POST[$nomCamp])) {
return trim(htmlspecialchars($_POST[$nomCamp]));
}
else {
$errors[$nomCamp] = "El $nomCamp és requerit";
return null;
}
}
guarde en l’array errors el possible error.
function isValidClass($nomCamp,$errors){
if (!isset($errors)) {
return '';
}
if (isset($errors[$nomCamp])) {
return 'is-invalid';
}
return 'is-valid';
}
function showError($nomCamp,$errors){
if (!isset($errors)) {
return '';
}
if (isset($errors[$nomCamp])) {
return "<div class='invalid-feedback'>$errors[$nomCamp]</div>";
}
return "<div class='valid-feedback'>All correct</div>";
}
funcions per a retornar el html necesari per a pintar els errors en bootsrap
extract($_POST,EXTR_PREFIX_SAME,'old');
genere variables old_nomdeCamp per als valors anterior
<div class="form-group">
<label for="assigned_to">Assignada a:</label>
<input name="assigned_to" type="text" class="form-control <?= isValidClass('assigned_to',$errors) ?>" id="assigned-to" aria-describedby="assignedHelp" placeholder="Enter person assigned to" value="<?= $old_assigned_to??'' ?>">
<small id="assignedHelp" class="form-text text-muted">Task's assigned to.</small>
<?= showError('assigned_to',$errors) ?>
</div>
mostre els errors utilitzant les funcions anterior i el camp old_title
Pujada de fitxers
Es un cas especial. En primer lloc en el formulari cal utilitzar l’atribut enctype=”multipart/form-data” i el metode POST. Per al fitxer s’utilitza una etiqueta <input type=”file”>.
<!DOCTYPE html>
<html>
<body>
<form action="procesar_subida.php" method="post" enctype="multipart/form-data">
Escoja un fichero
<input type="file" name="fichero">
<input type="submit" value="Subir fichero">
</form>
</body>
</html>
En l’script que reb el fitxer la variable global $_FILES conté la informació sobre el fitxer en un array bidimensional.
Elements del array del fitxer pujat
- $_FILES[‘imatge’][‘tmp_name’]: lloc i nom de l’arxiu temporal en el servidor.
- $_FILES[‘imatge’][‘name’]: Nom original del fitxer en la màquina client.
- $_FILES[‘imatge’][size’]: Grandària en bytes del fitxer pujat.
- $_FILES[‘imatge’][type’]: Tipus MIME associat al fitxer. Per exemple, “image/gif” o “text/plain”.
- $_FILES[‘imatge’][error’]: Codi d’error associat al fitxer pujat.
<?php
$tam = $_FILES["fichero"]["size"];
if($tam > 256 *1024){
echo "<br>Demasiado grande";
return;
}
echo "Nombre del fichero: " . $_FILES["fichero"]["name"];
echo "<br>Nombre temporal del fichero en sel servidor: " . $_FILES["fichero"]["tmp_name"];
$res = move_uploaded_file($_FILES["fichero"]["tmp_name"],"subidos/" . $_FILES["fichero"]["name"]);
if($res){
echo "<br>Fichero guardado";
} else {
echo "<br>Error";
}
Grandària del fitxer a pujar
- Podemos fixar el límit de grandària en el fitxer php.ini
- http://php.net/manual/es/ini.core.php#ini.upload-max-filesize
- També podem fixar el límit en el propi formulari
- A través d’un camp ocult (type=”hidden”) denominat MAX_FILE_SIZE.
<input type="hidden" name="MAX_FILE_SIZE" value="1000000">
Ruta temporal del fitxer pujat
- En carregar un arxiu, es guardarà en una ubicació temporal indicada per l’opció upload_tmp_dir en el php.ini.
- http://php.net/manual/es/ini.core.php#ini.upload-tmp-dir
- Si no movem l’arxiu o ho canviem de nom, quan acabe l’execució del script, este serà eliminat.
Problemes més habituals
- Especificar en upload_tmp_dir un directori al com no es té accés
- La directiva memory_limit té un valor molt baix o inferior a upload_max_filesize
- La directiva max_execution_time té un valor baix i el script ho excedeix durant la pujada del fitxer
- La directiva post_max_size té un valor baix i el teu fitxer ho excedeix. El seu valor ha de ser major a upload_max_filesize
Codis d’error
- El primer que caldrà fer és comprovar el codi d’error del fitxer pujat ($_FILES[‘imatge’][error’])
- Si és diferent de UPLOAD_ERR_OK hi ha hagut algun problema
- Veure els codis d’error:
- http://php.net/manual/es/features.file-upload.errors.php
Comprovació del tipus de fitxer
- A continuació comprovarem que el tipus myme està dins dels esperats:
if ($_FILES['imatge']['type'] !== 'image/gif') {
echo 'Error: No es tracta d'un fitxer .GIF.';
exit;
}
Evitar atacs
- Ataque comú:
- http://seclists.org/bugtraq/2000/sep/55
- Per a evitar aquest tipus d’atacs es va incloure la funció:
- bool is_uploaded_file ( string nom_arxiu )
- Retorna true si l’arxiu donat va ser carregat a través d’HTTP POST
- Li passarem el paràmetre $_FILES[‘imatge’][‘tmp_name’]
Moure l’arxiu pujat
- Per a moure l’arxiu temporal a la seua ubicació correcta usem la funció
- bool move_uploaded_file(string nom_arxiu, string destinació)
- Aquesta funció s’assegura que l’arxiu siga un arxiu carregat vàlid.
- Si existeix un arxiu destine amb el mateix nom, est serà sobreescrit.
Evitar sobrescritures
- Abans de moure l’arxiu pujat és convenient comprovar que no existeix un arxiu amb el mateix nom
- bool is_file(string nom_arxiu)
- Li passarem $_FILES[‘imatge’][‘name’]
- En cas que existisca caldrà canviar el nom, per exemple afegint una marca de temps com a prefix
Fitxers
Obrir un arxiu
Els arxius en PHP s’obrin amb la funció fopen(), que requereix dos paràmetres: l’arxiu que es vol obrir i la manera en què obrir l’arxiu. La funció retorna un punter en l’arxiu si és satisfactòria o zero si no ho és. Els arxius s’obrin per a realitzar operacions de lectura o escriptura.
$fp = fopen("miarchivo.txt", "r");
Si no és possible obrir l’arxiu, retorna zero, per això és freqüent utilitzar aquesta funció en una condició:
if (!$fp = fopen("miarchivo.txt", "r")){
echo "No se ha podido abrir el archivo";
}
Es pot obrir un arxiu però també una URL externa, ja que fopen() realment el que fa és crear una connexió, per això cal tancar-la posteriorment.
$fp = fopen("http://localhost:8000", "r");
Les maneres d’accés existents per a fopen és poden trobrar a la documentació oficial.
Llegir un arxiu
Una vegada obert l’arxiu, el llegirem i guardar els seus continguts en una variable amb fread():
$file = "miarchivo.txt";
$fp = fopen($file, "r");
$contents = fread($fp, filesize($file));
La variable contents guardarà el contingut que obtinguem amb la funció fread(). Aquesta funció requereix dos paràmetres, l’arxiu obert i la longitud que volem llegir d’aquest arxiu (en bytes). En aquest cas hem emprat la funció filesize() per a obtindre la grandària de l’arxiu i així retornar tot el seu contingut.
Tancar un arxiu
Finalment, tancarem l’arxiu (no és obligatori però es recomana):
fclose($fp);
Escriure en un arxiu
Igual que per a llegir un arxiu, hi ha més d’una manera d’escriure en un. La forma més bàsica és utilitzar la funció fwrite() (o fputs(), que és el seu àlies):
$file = "miarchivo.txt";
$texto = "Hola que tal";
$fp = fopen($file, "w");
fwrite($fp, $texto);
fclose($fp);
Aquesta vegada hem emprat la manera w, que permet escriure sobreescrivint l’arxiu.
Podem limitar la longitud de dades que volem escriure (totes les dades que hi havia en l’arxiu s’esborraran per complet igualment):
$file = "miarchivo.txt";
$texto = "Hola que tal";
$fp = fopen($file, "w");
fwrite($fp, $texto, 4); // Escribirá sólo: Hola
Si l’arxiu *^miarchivo.txt^^ no existeix, es crearà automàticament amb la manera w de la funció fopen.
Punter d’arxiu
Un punter d’arxiu (file pointer o handle) és una variable que fa referència a un arxiu. És una variable que apunta a un arxiu en concret, i normalment s’obté quan s’obri amb fopen().
PHP i la seua recol·lecció de fems tanca tots els punters d’arxius al final de l’execució del script, encara que es considera una bona pràctica tancar els arxius manualment amb fclose().
A més d’apuntar a un arxiu, apunta a una posició concreta en aqueix arxiu. En la majoria dels casos quan s’obri un arxiu el punter apunta al principi (posició 0) o al final de l’arxiu.
La funció feof() és utilitzada amb freqüència en el maneig d’arxius en PHP. Aquesta funció comprova si el punter es troba al final de l’arxiu. S’utilitza quan es recorre un arxiu línia per línia o per a la lectura de grans arxius, mitjançant un condicional:
$archivo = "miarchivo.txt";
// Abrimos el archivo
$fp = fopen($archivo, "r");
// Loop que parará al final del archivo, cuando feof sea true:
while(!feof($fp)){
echo fread($fp, 4092);
}
El codi anterior només carregarà 4kb de dades de vegada, la qual cosa redueix l’ús de memòria per a grans arxius.
Obtindre informació d’un arxiu
Es pot obtindre informació d’un arxiu a més del seu contingut: grandària, última vegada que s’ha accedit o modificat, nombre de links, etc. La funció principal per a obtindre aquesta informació és amb la funció stat(), en aquesta taula es poden veure els 12 elements que retorna el array.
$file = "miarchivo.txt";
$texto = "Todos somos muy ignorantes, lo que ocurre es que no todos ignoramos las mismas cosas.";
$fp = fopen($file, "w");
fwrite($fp, $texto);
$datos = stat($file);
echo $datos[3] . "<br>"; // Número de enlaces, 1
echo $datos[7] . "<br>"; // Tamaño en bytes, 85
echo $datos[8] . "<br>"; // Momento de último acceso, 1444138104
echo $datos[9] . "<br>"; // Momento de última modificación, 1444138251
Funcions de directoris
Les funcions de directoris vénen de l’extensió directories de PHP. Hi ha un total de 9 funcions disponibles que podeu consultar en la documentació