PHP Paso de datos con formularios

De MediaWiki
Ir a la navegación Ir a la búsqueda

Introdución

  • Sabemos que a través da etiqueta <form> podemos enviar datos dunha páxina a outra.
Os principais elementos html que poden enviar información dentro dun formulario son os indicados neste enlace.


  • Dentro da etiqueta form temos:
  • O atributo method que pode tomar os valores get ou post.
  • O atributo action que indica a páxina que ten que procesar os datos enviados polo formulario. Aquí poñeremos unha páxina php e moitas veces será a mesma páxina que ten o formulario, polo que podemos empregar como action: $_SERVER['PHP_SELF'] da forma:
1  <form name="form1" action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST">
2 
3  </form>


  • Nota: Para pasar datos dunha páxina a outra tamén podemos empregar formularios con campos ocultos (hidden).

Método GET

Os datos que aparecen despois do signo '?' son parámetros:?q=php&ie=utf-8&oe=utf-8


  • Desvantaxes:
  • Calquera persoa pode ver os valores das variables polo que dito método non é moi seguro.
  • O usuario pode cambiar o valor de ditas variables polo que pode suceder que o sitio amose ou faga algo diferente do permitido.
  • O usuario pode obter información non actual se usa unha URL con datos non actualizados.
  • O tamaño do que podemos enviar na URL está limitado (depende de cada navegador, pero pode estar entre 2KB-8KB)
  • Vantaxes:
  • A través da URL podemos pasar datos dunha páxina a outra sen necesidade de ter un formulario por medio.


  • As variables que se pasan desta forma poden ser referenciadas en PHP a través da matriz global $_GET['param'].


  • Vexamos agora un exemplo utilizando un formulario:

Arquivo: UD2_Form_Ex1_eleccion_pelicula.php

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 2     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 3 <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
 4     <head>
 5         <meta charset="utf-8" />
 6         <title>Formulario</title>
 7         <style>
 8             .formularios{
 9                 width: 30%;
10                 margin: 0px auto;
11             }
12             .etiqueta{
13                 float: left;
14                 width: 190px;
15             }
16             .botonesformularios{
17                 width: 50%;
18                 margin: 30px auto;
19                 text-align: center;
20             }
21             select {
22                 width:50%;
23             }
24         </style>
25     </head>
26     <body>
27         <form id="frmPelicula" class="formularios" method="get" action="UD2_Form_Ex1_datos_pelicula.php">
28             <div>    
29                 <div class='etiqueta'>Película preferida:</div>
30                 <select name="lstPelicula"><option>E.T. El extraterrestre</option><option>Supermán</option></select>
31             </div>                
32             <div>
33                 <div class='etiqueta'>Xénero preferido:</div>
34                 <select name="lstXenero"><option>Acción</option><option>Ciencia ficción</option></select>
35             </div>                
36             <div class="botonesformularios">
37                 <input type='submit' value="ENVIAR" />
38             </div>
39         </form>
40 
41     </body>    
42 </html>
43 </php>
  • Fixarse que enviamos os datos utilizando o método get.

Arquivo: UD2_Form_Ex1_datos_pelicula.php

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 2     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 3 
 4 <html>
 5     <head>
 6         <meta charset="UTF-8">
 7         <title></title>
 8     </head>
 9     <body>
10         <?php
11         echo 'Os datos que escolliches son:<br/><br/>';
12         
13         $pelicula = $_GET['lstPelicula'];
14         $xenero = $_GET['lstXenero'];
15 
16         printf("PELICULA:%s<br/>XÉNERO:%s",$pelicula,$xenero);
17         ?>
18         
19     </body>
20 </html>


  • NOTA: Se queredes visualizar o conxunto de valores que temos en $_GET podedes empregar a función var_dump da forma: var_dump($_GET);

Método POST

  • No método post de envío de datos, os mesmos van 'dentro' da páxina web, no corpo do formulario, e polo tanto non son visibles para o usuario.
  • Tamén está limitado o tamaño do que se pode enviar (por exemplo se enviamos un arquivo adxunto) a un tamaño de 8MB.
Podemos cambiar dito límite:
  • Modificando a directiva post_max_size do arquivo php.ini de PHP.
  • Modificando o arquivo .htaccess do noso sitio web e engadindo a directa: php_value post_max_size 20M (isto é necesario no caso de aloxamentos compartidos ou se non temos acceso ao arquivo php.ini).
Nota: No caso de subir arquivos tamén pode ser necesario modificar a directiva upload_max_filesize do arquivo php.ini
Podedes ver neste enlace como aumentar o tamaño dos arquivos subidos nun aloxamento compartido.


  • As variables que se pasan desta forma poden ser referenciadas en PHP a través da matriz global $_POST['param'].


  • Modificamos o exemplo anterior para que envíe os datos con POST:

Arquivo: UD2_Form_Ex2_eleccion_pelicula.php

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 2     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 3 <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
 4     <head>
 5         <meta charset="utf-8" />
 6         <title>Formulario</title>
 7         <style>
 8             .formularios{
 9                 width: 30%;
10                 margin: 0px auto;
11             }
12             .etiqueta{
13                 float: left;
14                 width: 190px;
15             }
16             .botonesformularios{
17                 width: 50%;
18                 margin: 30px auto;
19                 text-align: center;
20             }
21             select {
22                 width:50%;
23             }
24         </style>
25     </head>
26     <body>
27         <form id="frmPelicula" class="formularios" method="post" action="UD2_Form_Ex2_datos_pelicula.php">
28             <div>    
29                 <div class='etiqueta'>Película preferida:</div>
30                 <select name="lstPelicula"><option>E.T. El extraterrestre</option><option>Supermán</option></select>
31             </div>                
32             <div>
33                 <div class='etiqueta'>Xénero preferido:</div>
34                 <select name="lstXenero"><option>Acción</option><option>Ciencia ficción</option></select>
35             </div>                
36             <div class="botonesformularios">
37                 <input type='submit' value="ENVIAR" />
38             </div>
39         </form>
40 
41     </body>    
42 </html>
43 </php>
  • Fixarse que enviamos os datos utilizando o método post.

Arquivo: UD2_Form_Ex2_datos_pelicula.php

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 2     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 3 
 4 <html>
 5     <head>
 6         <meta charset="UTF-8">
 7         <title></title>
 8     </head>
 9     <body>
10         <?php
11         echo 'Os datos que escolliches son:<br/><br/>';
12         
13         $pelicula = $_POST['lstPelicula'];
14         $xenero = $_POST['lstXenero'];
15 
16         printf("PELICULA:%s<br/>XÉNERO:%s",$pelicula,$xenero);
17         ?>
18         
19     </body>
20 </html>



  • NOTA: Se queredes visualizar o conxunto de valores que temos en $_POST podedes empregar a función var_dump da forma: var_dump($_POST);

Comprobando se hai datos

  • Normalmente a páxina que recibe os datos vai facer algún tipo de tratamento con eles (por exemplo, facer unha alta nunha táboa dunha base de datos, consultar en base aos criterios recibidos,...)
  • Polo tanto será necesario realizar algún tipo de validación para comprobar se se envían os datos necesarios e estes teñen un formato correcto.
Lembrar repasar as funcións que comproban se un campo ten datos e as funcións que comproban o tipo de dato dunha variable.


1 if (empty($_GET)) die('Non se enviaron datos');  // Isto é válido para action=get no formulario
2 if (!isset($_POST["enviar"]))  die('Non se enviaron datos');  // Isto é válido para action=post no formulario, sendo enviar o nome do botón que xera o action do formulario

Nota: A función die ao igual que a función exit finaliza a páxina php.

O tratamento que queiramos darlle á páxina cando non se envíen datos será decidido polo programador, pudendo enviarlle un aviso ao usuario en enviando de novo a páxina do formulario.
Tede que ter en conta que aínda que fagades unha validación dos campos utilizando javascript, isto pódese saltar, xa que a páxina está no cliente. Ademais un usuario podería cargar a páxina de tratamento da información (a que está indicada no action do formulario) sen pasar polo formulario.


  • O segundo será verificar que os campos teñan algún valor (se é obrigatorio) e que o tipo de dato sexa o correcto. Para verificar o tipo de dato coas funcións is_XXXXX (coma por exemplo is_string) non se poden utilizar con datos que veñan a través dun formulario, xa que todos eles son avaliados como de tipo String.
É dicir, un dato que garde a idade, por exemplo, se teño a variable $idade=3; e aplico a función is_string($idade) vai devolver false, pero se o dato da idade (valor 3) ven dun formulario e aplico a mesma función is_string($_POST['txtIdade']) vai devolver true.
1 if (empty($_POST['txtNome'])) echo('Campo nome non ten datos');
  • IMPORTANTE: Todos os datos que veñen no array global $_POST son de tipo String. Teremos que facer un 'cast' ao tipo de dato ou ven utilizar a función filter_var que será explicada a continuación.
Facendo o cast podemos saber se é un número ou non da seguinte forma:
1 if (ctype_digit($_POST['txtIdade'])){
2      $idade = (int)$_POST['txtIdade'];
3 }
4 else {
5      echo 'Non é numérico integer';
6 }
Usamos a función ctype_digit para comprobar se temos díxitos numéricos no dato enviado.
O cast (int) vai devolver 0 en caso de que non poida convertelo a Integer.


Comprobando se chegan datos de varios formularios diferentes á mesma páxina

  • Imaxinemos o caso de que temos unha páxina de nome primeira.php. Dita páxina ten un formulario cuxo 'action' apunta a unha segunda páxina de nome: segunda.php
Imaxinemos que dita páxina segunda.php ten a súa vez un formulario cuxo action apunta a ela mesma.
Neste caso temos que identificar 3 posibles casos cando carguemos a páxina segunda.php:
  • A páxina se carga vindo do primeiro formulario. Para identificar este caso podemos facer uso do botón de tipo 'submit' e definilo da seguinte maneira:
<input name='btnEnviar1' type='submit' value='Envía datos páxina 1' />
Na páxina segunda.php podemos poñer a seguinte condición para saber se ven da primeira.php:
if (!empty($_POST['btnEnviar1'])) { } => Sabemos que vimos da primeira
  • A páxina se carga vindo de segunda.php
Podemos facer algo parecido á anterior e definir un name para o botón que fai o submit dende a segunda:
<input name='btnEnviar2' type='submit' value='Envía datos páxina 1' />
Na páxina segunda.php podemos poñer a seguinte condición para saber se ven da segunda.php:
if (!empty($_POST['btnEnviar2'])) { } => Sabemos que vimos da segunda páxina.
  • Se cargamos a páxina segunda.php directamente, o sabemos porque:
if (empty($_POST['btnEnviar1']) && empty($_POST['btnEnviar2'])) { } => Sabemos que cargamos a páxina directamente sen vir dende primeira.php nin de segunda.php

Función para validar / corrixir campos

  • No cliente:
* Para validar os campos podemos empregar javascript e algún dos atributos que teñen as etiquetas HTML.
Calquera das dúas formas son vulnerables de que un usuario salte as regras de validación, polo que suele ser necesario facer a validación no servidor por seguridade (ver máis adiante métodos de ataque por formularios).
A nivel de HTML temos as seguintes propiedaes:
  • required: Impide campos baleiros.
  • Lonxitude de cadeas con atributos coma maxlength, max, min...
  • Atributo type para establecer o tipo de dato que vai levar a caixa de texto: mail, text, number, date ...
  • Atributo accept para os input de tipo File que van subir un arquivo ao servidor. Con este atributo podemos especificar o tipo de arquivos que está permitido subir (exemplo accept=".pdf"),




  • No servidor:
  • O proceso de validación-corrección de campos dun formulario soe ser dar bastante traballo.
Para facernos a vida un pouco máis doada, temos unha extensión en PHP que permite a validación de campos dunha forma moi sinxela.
A función filter_var permítenos filtrar unha variable segundo o filtro indicado.
Parámetros:
  • $var: Variable que se quere filtrar
  • $filter: Filtro que se desexa aplicar. Será una constante numérica (indicadas a continuación)
  • $options: Conxunto de opcións que modifican o funcionamento do filtro. Pode ser unha constante numérica ou un array
Valor devolto:
  • O dato filtrado ou false se o filtro faia.


Filtros para a validación

  • FILTER_VALIDATE_BOOLEAN => Valida a variable coma un booleano.
  • FILTER_VALIDATE_EMAIL => Valida a variable coma unha dirección de correo electrónico correcta.
  • FILTER_VALIDATE_FLOAT => Valida que a variable sexa do tipo float.
  • FILTER_VALIDATE_INT => Valida a variable coma un número enteiro.
  • FILTER_VALIDATE_IP => Valida a variable coma unha dirección IP.
  • FILTER_VALIDATE_REGEXP => Valida a variable contra unha expresión regular enviada na variable de opcións.
  • FILTER_VALIDATE_URL => Valida o valor coma unha URL de acordo coa RFC 2396.


Filtros para a corrección

  • FILTER_SANITIZE_EMAIL => Elimina tódolos caracteres execepto letras, números e !#$%&’*+-/=?^_`{|}~@.[].
  • FILTER_SANITIZE_ENCODED => Codifica a cadea coma unha URL válida.
  • FILTER_SANITIZE_MAGIC_QUOTES => Aplica a función addslashes.
  • FILTER_SANITIZE_NUMBER_FLOAT => Elimina tódolos caracteres excepto números, +- e opcionalmente ,.eE Se non poñemos un flag, non admitirá números decimais.
  • FILTER_SANITIZE_NUMBER_INT => Elimina tódolos caracteres excepto números e os signos + -
  • FILTER_SANITIZE_SPECIAL_CHARS => Escapa caracteres HTML e caracteres con ASCII menor a 32. A empregar en vez FILTER_SANITIZE_STRING.
Normalmente con filter_var($valor,FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES)
  • FILTER_SANITIZE_STRING => Deprecated => vai ser quitada. Elimina etiquetas, opcionalmente elimina ou codifica caracteres especiais. Pode levar como terceiro parámetro algún destes flag´s:
  • FILTER_FLAG_NO_ENCODE_QUOTES - Non codifica as comillas simples ou dobres (non as substitúe por códigos &#39 => ' por exemplo)
  • FILTER_FLAG_STRIP_LOW - Elimina caracteres cun código ASCII < 32
  • FILTER_FLAG_STRIP_HIGH - Elimina caracteres cun código ASCII > 127
  • FILTER_FLAG_ENCODE_LOW - Codifica caracteres cun código ASCII < 32
  • FILTER_FLAG_ENCODE_HIGH - Codifica caracteres cun código ASCII > 127
  • FILTER_FLAG_ENCODE_AMP - Codifica o carácter "&" con &
Nota: Pódense combinar varios flag´s con |
  • FILTER_SANITIZE_STRIPPED => Alias do filtro anterior.
  • FILTER_SANITIZE_URL => Elimina tódolos caracteres excepto números, letras y $-_.+!*’(),{}|\\^~[]`<>#%”;/?:@&=

Exemplos

Validando

  • Validando un número enteiro
1 <?php  
2     $var = 123;  
3     echo filter_var($var, FILTER_VALIDATE_INT);  
4 ?>
Devolve o número xa que é un enteiro.
  • Validando un número enteiro
1 <?php  
2     $var = -2.343;  
3     if(filter_var($var, FILTER_VALIDATE_INT) === false){  
4         echo 'Valor incorrecto';  
5     }else{  
6         echo 'Valor correcto';  
7     }  
8 ?>
Devolve 'Valor incorrecto' xa que non é un enteiro e polo tanto a función devolve false.
  • Validando un número enteiro
1 <?php  
2     $var = -2;  
3     if(filter_var($var, FILTER_VALIDATE_INT) === false){  
4         echo 'Valor incorrecto';  
5     }else{  
6         echo 'Valor correcto';  
7     }  
8 ?>
Devolve 'Valor correcto'.


  • Validando un número flotante
1 <?php
2     $var = '-1.45';  
3     if(filter_var($var, FILTER_VALIDATE_FLOAT) === false){  
4         echo 'Valor incorrecto';  
5     }else{  
6         echo 'Valor correcto';  
7     }  
8 ?>
Devolve 'Valor correcto'.


  • Validando un email
1 <?php  
2     $var = 'casa@hotmail';  
3     if (!filter_var($var,FILTER_VALIDATE_EMAIL)){
4         echo "Valor incorrecto";
5     }else{  
6         echo 'Valor correcto';  
7     }  
8 ?>
Devolve 'Valor incorrecto'.

Corrixindo os valores

  • Cunha serie de filtros (SANITIZE) podemos eliminar aqueles caracteres que non queremos que aparezan nun determinado campo.
  • Eliminando etiquetas dentro dun text-area.
1 <?php  
2     $var = '<isto non está permitido> Isto si aparece';  
3     echo filter_var($var,FILTER_SANITIZE_STRING);
Devolve 'Isto si aparece'.
NOTA: Aínda que se segue empregando, a opción FILTER_SANITIZE_STRING está deprecated e se recomenda empregar esta forma:
1 <?php  
2     $var = '<isto non está permitido> Isto si aparece';  
3     echo filter_var($var, FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES);


  • Corrixindo un número flotante.
1 <?php
2 $number="53.3pp";
3 
4 var_dump(filter_var($number, FILTER_SANITIZE_NUMBER_FLOAT,
5 FILTER_FLAG_ALLOW_FRACTION));  // Este flag fai que o caracter 'punto' sexa válido (separador de decimais)
6 ?>
Devolve '53.3'.

Establecendo criterios

  • Podemos indicar que rango de valores son aceptables polo filtro.
  • Validando un número enteiro dentro dun rango
 1 <?php  
 2     $var = -200;  
 3     $opciones = array(
 4         'options' => array(
 5         'min_range' => 10,
 6         'max_range' => 100
 7     ));
 8       
 9     if(filter_var($var, FILTER_VALIDATE_INT, $opciones) === false){  
10         echo 'Valor incorrecto';  
11     }else{  
12         echo 'Valor correcto';  
13     }  
14 ?>
Devolve 'Valor incorrecto'


  • Podemos facer unha modificación para que en caso incorrecto devolva sempre un valor 'por defecto':
 1 <?php  
 2     $var = -200;  
 3     $opciones = array(
 4         'options' => array(
 5         'default' => 3, // valor a retornar se o filtro faiaa
 6         'min_range' => 10,
 7         'max_range' => 100
 8     ));
 9       
10     $var = filter_var($var, FILTER_VALIDATE_INT, $opciones);
11     echo $var;
12 
13 ?>
Neste caso $var valerá '3' en caso de que non se cumpra o filtro.


  • No caso dun número flotante podemos indicar cal é o carácter separado dos díxitos decimais.
 1 <?php  
 2     $var = '1,23';  
 3     $opciones = array(
 4         'options' => array(
 5         'decimal' => ','
 6     ));
 7       
 8     if(filter_var($var, FILTER_VALIDATE_FLOAT, $opciones) === false){  
 9         echo 'Valor incorrecto';  
10     }else{  
11         echo 'Valor correcto';  
12     }  
13 ?>
Devolve 'Valor correcto'


  • Nota: Podemos facer uso da función filter_input que permite obter un dato dun formulario e validar ao mesmo tempo.

Seguridade no uso dos formularios

  • Cando manexamos formularios temos que ter precaución co manexo de datos, xa que un usuario malintencionado podería poñer en risco o noso sitio web.

Inxección de CSS

  • Este problema aparece normalmente cando temos unha caixa de texto / textarea no que imos a engadir/consultar, cos datos que veñen, a unha táboa dunha base de datos.


Unha consulta clásica de validación de usuario podería ser:
  • $_POST['usuarios'] = 'angel'
  • $_POST['password'] = '12345678'
1 <?php
2             $nome=$_POST['usuarios'];
3             $password=$_POST['password'];
4                       
5             printf("select count(*) from USUARIOS where nome='%s' and password='%s'",$nome,$password);
6 ?>
Neste caso se a consulta devolve 1 entón é un usuario da nosa base de datos.
A consulta a executar no xestor de base de datos sería: select count(*) from USUARIOS where nome='angel' and password='12345678'
  • Agora modifiquemos o campo password e imaxinemos que o usuario pon isto:
  • $_POST['password'] = "' or password like '%";
O resultado da consulta sería: select count(*) from USUARIOS where nome='angel' and password=' ' or password like '%'
  • Como vemos estamos saltándonos a seguridade debido a que:
  • O usuario pode engadir máis texto do permitido (incluso poñendo restricións de javascript, estas poden ser saltadas).
  • O usuario pode engadir 'texto' que non debería estar permitido.

Nota: En vez de modificar unha consulta (como no exemplo) podería executar ordes SQL como de inserción, borrado ou modificación de rexistros.

Solución a inxección de CSS

  • A mellor solución é utilizar procedementos almacenados. Sempre que o xestor o permite.
Vexamos un exemplo en Mysql:
1 DELIMITER //
2 CREATE PROCEDURE country_hos
3 (IN log VARCHAR(20),IN pass VARCHAR(30))
4 BEGIN
5   SELECT count(*) FROM USUARIOS
6   WHERE login = log and password = pass;
7 END //
8 DELIMITER ;
Nesta solución temos:
  • O usuario aínda que envíe máis información so se vai coller 20 caracteres para o login e 30 para o password.
  • Aínda que engada 'código SQL' non se vai a executar xa que dito código vai ir todo na campo password.
Loxicamente isto non quita que busquemos formas de validar os datos introducidos.


  • Outra opción é a de utilizar funcións que provea o S.X.B.D. utilizado.
No caso de mysql temos a función mysqli_real_escape_string. O veremos cando cheguemos á sección de acceso a datos.


Cross-Site Scripting (XSS)


  • Este tipo de ataque é debido a que un usuario engade código script (coma javascript) á información que imos amosar na páxina web.
Polo tanto teremos que vixiar os datos que VISUALICEMOS na páxina.


Un exemplo sería se introducimos dentro dun text-area este código:
1 <script>window.location="http://www.google.es";</script>
O que vai pasar é que o navegador cando cargue a páxina vai cargar a páxina de google indicada no código javascript.
Fixarse no perigo que supón xa que un atacante pode levar a un usuario a outro sitio web diferente.
  • Outro exemplo de ataque por URL:
http://localhost/PhpOlaMundo/Formularios/datosFormulario.php?texto=<script>window.location="http://www.google.es";</script>
Se amosamos o dato: $_GET['texto'] teremos o mesmo problema que o anterior.


  • Este código pode vir embebido nunha folla de estilos CSS, nun atributo dunha etiqueta HTML, dentro do corpo da páxina,na URL...

Importante: Fixarse que neste tipo de ataques temos que evitar que o que visualizamos leve código de script embebido. É diferente ao anterior no que supoñía un ataque á base de datos.


  • Outro exemplo é se empregamos a variable $_SERVER['PHP_SELF'] como parámetro nun action dun formulario da forma:
1 <form method="post" action="<?php echo $_SERVER['PHP_SELF']); ?>" name="formulario_perfumes">
2 
3 ......
4 
5 </form>


Solucións ao ataque Cross-Site Scripting (XSS)

  • Igual que no caso anterior podemos utilizar a función filter_var cos filtros FILTER_SANITIZE_MAGIC_QUOTES ou FILTER_SANITIZE_STRING para eliminar dita posibilidade.
  • En PHP podemos facer uso das seguintes funcións:
  • Función strip_tags: Dita función fai o mesmo que FILTER_SANITIZE_STRING e ademais podemos indicar cales etiquetas son permitidas.
  • Función htmlentities: Dita función substitúe os caracteres das etiquetas en códigos HTML para a súa visualización.
Se aplicamos dita función ao exemplo anterior teríamos esta saída coma código fonte de páxina: & lt;script& gt;window.location=& quot;http://www.google.es& quot;;& lt;/script& gt;
Fixarse que os caracteres < e > son substituídos por & lt; e & gt; respectivamente.
(Hai posto un espazo en branco entre & e o código xa que se non a wiki o visualizará como html)


No caso do formulario poderíamos poñer:

<form method="post" action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>" name="formulario_perfumes">

......

</form> </syntaxhighlight>



Ataques CSRF (Cross-Site Request Forgery)



Normas xerais

  • Como normas xerais:
  • Nos formularios sempre debemos empregar a técnica que evite os ataques CSRF (será explicada na sección de sesión).
  • Empregaremos a función htmlentities para amosar datos ao usuario que veñan de formularios ou da base de datos.
  • Empregaremos a función real_escape_string sobre os datos que van ser gardados na base de datos. O veremos cando cheguemos á sección de acceso a datos.
  • Empregaremos filtros de validación para chequear o tipo e formato de datos que veñan dun formulario.
  • Empregaremos filtros de corrección cando queramos gardar na base de datos campos de texto no que queramos eliminar previamente os caracteres non permitidos.




Obtendo variables de formularios (post/get) e filtrado ao mesmo tempo

  • En PHP , a partires da versión 5, podemos recuperar o valor dun campo dun formulario e aplicarlle un filtro ao mesmo tempo.
A función que permite facer isto é filter_input.
  • Sintaxe:
1 mixed filter_input ( int $type , string $variable_name [, int $filter = FILTER_DEFAULT [, mixed $options ]] )
Parámetros:
  • type: INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER o INPUT_ENV.
  • variable_name: Nome da variable, ven ser o nome da control html en caso dun formulario.
  • filter: O ID do filtro a aplicar. Xa os vimos neste mesmo punto da wiki.
  • options: Array asociativo de opcións. Se o filtro acepta opcións, se poden engadir nun array asociativo baixo la clave "flags".
Valores devoltos:
  • En caso de éxito, valor da variable pedida, FALSE se o filtro faia ou NULL se a variable variable_name non está definida.
  • Por exemplo:
1 <?php
2 $buscar_html = filter_input(INPUT_GET, 'buscar', FILTER_SANITIZE_SPECIAL_CHARS);
3 $buscar_url = filter_input(INPUT_GET, 'buscar', FILTER_SANITIZE_ENCODED);
4 echo "Has buscado $buscar_html.\n";
5 echo "<a href='?buscar=$buscar_url'>Buscar de nuevo</a>";
6 ?>


Redireccionamento de páxinas

  • Pode ser interesante cando realizamos un tratamento cos datos que veñen dun formulario ter a posibilidade de mandar ao usuario a unha páxina concreta.
Imaxinemos o caso de que intentamos cargar a páxina de tratamento dun formulario previo ou que enviamos datos que non están completos. Neses casos queremos volver a enviar ao usuario á páxina de entrada de datos.
Dita función serve para enviar encabezados ao navegador do cliente. Uns destes encabezados serve para redireccionar a páxina do clienta. A forma de facelo é:
1 <?php
2 header('Location: http://www.nova_paxina.es/');
3 exit;
4 ?>

Importante: É NECESARIO QUE A CHAMADA Á FUNCIÓN header SE FAGA ANTES DE ENVIAR NINGÚN DATO AO CLIENTE.

Quere isto dicir que ao cliente non lle pode chegar ningunha etiqueta HTML antes de facer o header-location. Non vale se temos un echo/printf de calquera cousa ou código HTML enviado antes da orde header.


Ao instalalo teremos a posibilidade de capturar os datos de cabeceira da páxina se imos ao menú Herramientas => Live HTTP Headers.
Coa función header imos poder manipular os datos enviados ao cliente dende o servidor.

Gardando imaxes no servidor

  • Nos formularios temos a posibilidade de subir arquivos ao servidor.
Un uso típico pode ser o de gardar documentos ou dar de alta produtos con imaxes asociadas. Neste último caso, o que gardaremos na base de datos será o nome do arquivo da imaxe (por exemplo 'imaxe.jpg') e cando recuperemos os datos da base de datos 'construiremos' unha etiqueta <img src='http://www.meusitio.es/IMAXES/imaxe.jpg' /> sendo IMAXES o cartafol onde están gardadas as imaxes no meu servidor web que atende a peticións coa URL: http://www.meusitio.es


  • O primeiro que teremos que facer é crear un cartafol onde queiramos deixar as imaxes no noso servidor web.
Como vimos na wiki nun punto anterior, o servidor web Apache 'apunta' fisicamente por defecto a '/var/www/html'.
Crearemos (podedes comprobar a onde apunta mirando o arquivo de configuración /etc/apache2/sites-available/000-default.conf, como está explicado na wiki) un cartafol de nome IMAXES e darémoslle permiso de escritura ao usuario que utiliza o servidor Apache para acceder ao sistema de arquivos (www-data). Isto último está explicado no seguinte punto da wiki.


  • Supoñemos que document_root apunta a /var/www/html e que estades a utilizar como servidor web Apache instalado de forma independente (sen XAMPP)
Supoñemos que estades a utilizar Apache como servidor web e que o usuario-grupo que utiliza é www-data
1 cd /var/www/html
2 mkdir IMAXES
3 chown www-data:www-data IMAXES
4 chmod 755 IMAXES


Se estiverades a utilizar XAMPP, teredes que mirar o arquivo de configuración do Apache que no caso de XAMPP é o httpd.conf que se atopa no cartafol /opt/lampp/etc
 1 <IfModule unixd_module>
 2 #
 3 # If you wish httpd to run as a different user or group, you must run
 4 # httpd as root initially and it will switch.
 5 #
 6 # User/Group: The name (or #number) of the user/group to run httpd as.
 7 # It is usually good practice to create a dedicated user and group for
 8 # running httpd, as with most system services.
 9 #
10 User daemon
11 Group daemon
12 </IfModule>
Nas liñas 10 e 11 se indica o usuario e grupo que utiliza o Apache de XAMPP para acceder aos cartafoles.
Deberedes cambiar a liña do script anterior de chown www-data:www-data IMAXES a chown daemon:daemon IMAXES



Páxina do formulario

  • A páxina onde se atopa o formulario para subir entre outros campos o campo imaxe debe ter as seguintes características.
Información obtida de www3schools.
 1 <!DOCTYPE html>
 2 <html>
 3 <body>
 4 
 5 <form action="GardaCampos.php" method="post" enctype="multipart/form-data">
 6     Selecciona unha imaxe para subir:
 7     <input type="file" name="fileToUpload" id="fileToUpload">
 8     <input type="submit" value="Subir Imaxe" name="btnSubirImaxe">
 9 </form>
10 
11 </body>
12 </html>

IMPORTANTE:

  • Liña 5: Debemos de escribir enctype="multipart/form-data" na etiqueta form en caso de querer subir arquivos a un servidor.
  • Liña 7: O elemento HTML para poder seleccionar unha imaxe debe ser de tipo file
  • Agora o Action do formulario pode 'apuntar' á mesma páxina ou a outra para procesar a subida do arquivo.


Páxina que procesa os datos

Pode ser a mesma do formulario ou outra diferente. No exemplo se chama 'GardaCampos.php'.
 1 <?php
 2     // Enviamos datos
 3 if(isset($_POST["btnSubirImaxe"])) {
 4     if(is_uploaded_file($_FILES["fileToUpload"]["tmp_name"])){  // Subido ao cartafol temporal do servidor
 5         $target_dir = $_SERVER['DOCUMENT_ROOT'] . "/IMAXES/";  // Gardaremos a imaxe no cartafol a onde apunta o servidor web máis /IMAXES => /var/www/html/IMAXES
 6         $target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);  // Colle o nome da imaxe. Isto é necesario se a imaxe ten unha ruta posta no seu nome. 
 7 
 8         $tipo = $_FILES["fileToUpload"]["type"];
 9         if ($tipo=='image/jpeg')   {  // Neste exemplo verficamos que o arquivo sexa unha imaxe jpg
10             if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
11                 echo "O arquivo ". basename( $_FILES["fileToUpload"]["name"]). " foi subido ao servidor.";
12             } else {
13                 printf("Houbo un erro subindo o arquivo:%s",$_FILES['fileToUpload']['error']);
14             }
15         }
16         else{
17             echo ("O tipo de arquivo non é jpg");
18         }
19     }
20     else{
21         printf("Houbo un erro subindo o arquivo:%s",$_FILES['fileToUpload']['error']);
22     }
23 
24 
25 }
26 ?>
  • En caso de erro, podemos ver o valor de $_FILES['fileToUpload']['error']:
  • UPLOAD_ERR_OK (0): Sen erros. O arquivo subiuse con éxito.
  • UPLOAD_ERR_INI_SIZE (1): O arquivo é máis grande que o tamaño máximo permitido especificado na configuración upload_max_filesize en php.ini.
  • UPLOAD_ERR_FORM_SIZE (2): O arquivo é máis grande que o tamaño máximo permitido especificado no formulario HTML mediante o atributo MAX_FILE_SIZE.
  • UPLOAD_ERR_PARTIAL (3): O arquivo subiuse parcialmente.
  • UPLOAD_ERR_NO_FILE (4): Non se subiu ningún arquivo. Isto pode ocorrer cando o campo de arquivo no formulario está baleiro ou se omite.
  • UPLOAD_ERR_NO_TMP_DIR (6): Falta o directorio temporal. Isto pode deberse a unha configuración incorrecta no servidor.
  • UPLOAD_ERR_CANT_WRITE (7): Non se puido escribir o arquivo no disco. Isto pode deberse a problemas de permisos no directorio de destino.
  • UPLOAD_ERR_EXTENSION (8): Unha extensión PHP detivo a subida do arquivo. Isto pode ocorrer cando unha extensión PHP específica bloquea a subida do arquivo.


Nota importante: Algunhas veces ['error'] sempre devolve false (0) e non se establece correctamente o error.
Para saber que pasa, temporalmente podemos poñer ao principio da páxina:
1     error_reporting(E_ALL);
2     ini_set('display_errors', 1);


  • ALgunhas función que pode ser de utilidade:
  • función basename devolve a última parte dunha ruta. Así se teño c:\carpeta1\carpeta2\arquivo.jpg devolverá 'arquivo.jpg'.
Por exemplo:
$target_dir = $_SERVER['DOCUMENT_ROOT'] . "/IMAXES/"; // Gardaremos a imaxe no cartafol a onde apunta o servidor web máis /IMAXES => /var/www/html/IMAXES
$target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]); // Colle o nome da imaxe. Isto é necesario se a imaxe ten unha ruta posta no seu nome.


Por exemplo:
$imageFileType = pathinfo($target_file,PATHINFO_EXTENSION);
  • A función getimagesize devolve un array con información da imaxe. En caso de que non sexa unha imaxe devolve false.
Por exemplo:
$check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
if($check !== false) {
echo "O arquivo é unha imaxe - " . $check["mime"] . ".";
} else {
echo "O arquivo non é unha imaxe.";
}


  • Debemos ter en conta que podemos subir outro tipo de arquivos, como documentos,...iso dependerá da nosa aplicación.
Podemos mellorar o noso código introducindo novas condicións.


  • Verificar se o arquivo existe antes de subilo:
1 <?php
2 if (file_exists($target_file)) {
3     echo "Ese arquivo xa existe.";
4     $uploadOk = 0;
5 } 
6 ?>


  • Limitar o tipo do arquivo:
1 <?php
2 $imageFileType = $_FILES["fileToUpload"]["type"];
3 if($imageFileType != "image/jpeg" && $imageFileType != "/image/png" && $imageFileType != "image/jpg"
4 && $imageFileType != "image/gif" ) {
5     echo "Soamente arquivos JPG, JPEG, PNG & GIF están permitidos.";
6     $uploadOk = 0;
7 } 
8 ?>


  • Limitar o tamaño do arquivo:
1 <?php
2  // Check file size
3 if ($_FILES["fileToUpload"]["size"] > 500000) {
4     echo "O arquivo ten máis de 500KB. Non se pode subir";
5     $uploadOk = 0;
6 } 
7 ?>
  • Para o tamaño do arquivo podemos empregar outra aproximación, que consiste en poñer un campo oculto antes do campo de tipo file da seguinte forma:
1 <form action="GardaCampos.php" method="post" enctype="multipart/form-data">
2     Selecciona unha imaxe para subir:
3     <input type="hidden" name="MAX_FILE_SIZE" value="10000" />   
4     <input type="file" name="fileToUpload" id="fileToUpload">
5     <input type="submit" value="Subir Imaxe" name="btnSubirImaxe">
6 </form>
O nome ten que ser MAX_FILE_SIZE e o value é o tamaño máximo en bytes.
Cando se procesa o formulario:
 1 <?php
 2 if(isset($_POST["btnSubirImaxe"])) {
 3     $target_dir = $_SERVER['DOCUMENT_ROOT'] . "/IMAXES/";  // Gardaremos a imaxe no cartafol a onde apunta o servidor web máis /IMAXES => /var/www/html/IMAXES
 4     $target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);  // Colle o nome da imaxe. Isto é necesario se a imaxe ten unha ruta posta no seu nome. 
 5     if ($_FILES['fileToUpload']['error']>0)
 6         die("Houbo un erro subindo o arquivo: " . $_FILES['fileToUpload']['error']);  // Neste exemplo dará de erro o valor 2 => UPLOAD_ERR_FORM_SIZE (visto anteriormente)
 7 
 8     ......
 9 
10 ?>


Comentar que o referente ao tamaño dos arquivos, imos ter a limitación imposta polo propio PHP.
Se estades non aloxamento compartido non ides poder modificar o arquivo de configuración para permitir subir arquivos de máis de 8MB.
Existen dúas entradas no arquivo de configuración php.ini que son:
upload_max_filesize = 2M;
post_max_size = 8M;
Estas directivas indican o tamaño máximo dun arquivo a subir e o tamaño máximo dun formulario incluíndo todos os seus campos.
Lembrar que o arquivo de configuración de PHP se atopa en:
  • /etc/php5/apache2/php.ini se estados a utilizar un servidor web Apache instalado independentemente.
  • /etc/php5/cli/php.ini se estados a utilizar XAMPP.
Se queredes aumentar o tamaño deberedes cambiar ambas directivas aos valores desexados.
Tedes no meu Blog unha entrada do que hai que facer no caso de estar nun aloxamento compartido.
No blog se explica os pasos para aplicar no Moodle, pero podería aplicarse en outros escenarios.


Teríamos varias opcións, sempre supoñendo que o aloxamento compartido deixe crear arquivos php.ini ou .htaccess que sobrescriban a configuración do servidor web Apache (isto normalmente é así).
  • Opción a)
Crear un arquivo php.ini no cartafol raíz do noso sitio web e poñer:
1 upload_max_filesize = 64M
2 post_max_size = 64M
3 max_execution_time = 300
No exemplo estamos a limitar en 64MB o tamaño da subida e o tempo de execución en 5 minutos.
  • Opción b)
Crear un arquivo .htaccess no cartafol raíz do noso sitio web e poñer:
1 php_value upload_max_filesize 64M
2 php_value post_max_size 64M
3 php_value max_execution_time 300
4 php_value max_input_time 300
No exemplo estamos a limitar en 64MB o tamaño da subida e o tempo de execución en 5 minutos.



Verificando os datos dun formulario

  • Unha forma rápida de verificar que certos datos dun formulario teñan información sería:
 1 <?php  
 2 // Campos obrigatorios
 3 $campos_obrigatorios = array('txtNome', 'txtEmail');
 4 
 5 // Comprobamos que os campos obrigatorios teñan datos
 6 $erro = false;
 7 foreach ($campos_obrigatorios as $campo) {
 8   if (empty($_POST[$campo])) $erro = true;
 9 }
10 
11 if ($erro) {
12   // Non existe algún dos campos obrigatorios
13 }

Creando arrays cos datos dun formulario

  • Algunhas veces, cando xeramos formularios dinamicamente, teremos que asignar un nome a cada campo do formulario. Pero neste tipo de formularios dinámicos non sabemos cantos 'nomes' imos xerar.
  • Imaxinemos unha páxina na que queremos xerar dinamicamente un formulario de alta de novos exemplares dun libro determinado.
Así temos un libros cos seus datos (titulo, isbn,...)
Dispoñemos de exemplares no que queremos gardar: estado (bo, mal, normal).
Cando eu dea de alta o libro vou dar os seus datos máis o número de exemplares. Imaxinemos que poño que do libro 'A illa do tesouro' teño 3 exemplares.
Este dato pode variar dun libro a outro e non sei de antemán cantos exemplares vou ter.
Se creo agora o formulario para eses 3 exemplares tería que crear algo así:
1 <input type='text' name='txtEstado1' />
2 <input type='text' name='txtEstado2' />
3 <input type='text' name='txtEstado3' />

Cando eu preme o 'action' do formulario os datos pasará á seguinte páxina e eu tería que saber cantos 'txtEstado?' se envían.

  • Unha forma moito máis sinxela de facelo é a seguinte:
  • Definimos o campo que vai levar o estado có seguinte nome: txtEstado[ ]
Creamos cada input desta forma:
1 for($cont=0;$cont<$numExemplares;$cont++){
2 <input type='text' name='txtEstado[]' />
3 }
  • Cando estes datos cheguen á seguinte páxina o dato: $_POST['txtEstado'] vai ser un array cos datos de todos os exemplares e xa podemos traballar con el como ata o de agora.




Exercicios propostos

  • Exercicio 1: Crea unha páxina cun formulario e dous campos, nome e idade. O método de envío debe ser aquel que o os datos van dentro do corpo do formulario. O destino do formulario é a mesma páxina (non vale empregar o URL escrita a man). O campo de nome non debe deixar escribir máis de vinte caracteres e o de idade dous caracteres. Terá un botón Enviar Datos. En caso de que se envíen os datos, amosará nunha lista os datos enviados. No caso de que non se envíe algún dato debe amosar un texto indicando que o dato é obrigatorio e amosar outra vez o formulario. Deberás verificar que o que se envía e un número (idade) de dous caracteres e un nome (cadea) quitando todos os caracteres que poidan dar lugar a algún tipo de ataque de inxección de código. Deberá amosar en forma de lista os datos enviados e non amosar o formulario.


  • Exercicio 2: Fai o necesario para xestionar as operacións de alta, baixa e modifcación dunha lista de froitas. Se debe asegurar que no texto da froita non se poida empregar nun ataque XSS ou de inxección de código. Se ven baleiro o campo de texto debe amosar o erro. Debe amosarse en forma de táboa a lista de froitas engadidas. Ao carón de cada froita debe aparecer un botón cun texto eliminar, que borre dito elemento da lista de froitas. Se non hai elementos na lista debe amosar o texto 'Cesta baleira'. Nun arquivo separado de nome operacionsFroitas, se gardará nunha variable o array de froitas e as funcións para verificar que o nome da froita sexa correcto e cambiar no nome os caracteres non permitidos. Se ven baleiro debe devolver false. Outra función para engadir unha froita ao array e se a froita xa estivera engadidad non facer nada, una función que dada unha froita, a elimine do array se existe. Dito arquivo é necesario para o funcionamento da páxina principal e sabemos que non vai cargarse nada máis que unha vez. Terá de nome operaciónsFroitas.php e estará gardado no cartafol /utilidades/.


  • Exercicio 3: Crea nun arquivo separado de nome /utilidades/usuarios, un array de datos de usuario (cada usuario ten: id, nome, tfno, email). Nese mesmo arquivo define unha función de nome obterUsuarioPorID que en base a un id, busque no array de usuarios, toda a información do usuario con ese id e que devolva toda a información dese usuario se existe. Se non existe debe devolver null. Amosa unha páxina de nome listaUsuarios, unha lista de nomes de usuario, en forma de enlace. Ao premer no nome, deberá amosar toda a información do usuario noutra páxina diferente de nome detalleUsuario . Esa segunda páxina terá un enlace para ir á primeira páxina e volver a listar os usuarios.
Se envías un id que non existe deberá amosar unha mensaxe. Comproba como podes modificar na URL o número de ID a buscar.










Solución Exercicios propostos

  • Solución Exercicio 1: Crea unha páxina cun formulario e dous campos, nome e idade. O método de envío debe ser aquel que o os datos van dentro do corpo do formulario. O destino do formulario é a mesma páxina (non vale empregar o URL escrita a man). O campo de nome non debe deixar escribir máis de vinte caracteres e o de idade dous caracteres. Terá un botón Enviar Datos. En caso de que se envíen os datos, amosará nunha lista os datos enviados. No caso de que non se envíe algún dato debe amosar un texto indicando que o dato é obrigatorio e amosar outra vez o formulario. Deberás verificar que o que se envía e un número (idade) de dous caracteres e un nome (cadea) quitando todos os caracteres que poidan dar lugar a algún tipo de ataque de inxección de código. Deberá amosar en forma de lista os datos enviados e non amosar o formulario.
<!DOCTYPE html>
<html lang="gl">
<head>
    <meta charset="UTF-8">
    <title>Formulario en Galego</title>
</head>
<body>
<?php
$nome = "";
$idade = "";
$erros = array();
$formulario_mostrado = true; // Variable para controlar si se muestra el formulario


if (isset($_POST['btnEnviar'])) {   // Vimos de premer o botón btnEnviar
    // Recoller e limpar os datos enviados
    $nome = filter_input(INPUT_POST, 'nome', FILTER_SANITIZE_STRING); // Deprecated. Mellor => filter_input(INPUT_POST, 'nome', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES);
    $idade = filter_input(INPUT_POST, 'idade', FILTER_SANITIZE_STRING);  // Deprecated. Mellor => filter_input(INPUT_POST, 'idade', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES);


    // Validación dos datos
    if (empty($nome)) {
        $erros[] = "O campo nome é obrigatorio.";
    } elseif (strlen($nome) > 20) {
        $erros[] = "O campo nome non pode ter máis de 20 caracteres.";
    }

    if (empty($idade)) {
        $erros[] = "O campo idade é obrigatorio.";
    } elseif (!(filter_input(INPUT_POST, 'idade', FILTER_VALIDATE_INT) && $idade > 0)) {
        $erros[] = "A idade debe ser un número positivo.";
    } elseif (strlen($idade) != 2) {
        $erros[] = "A idade debe ter exactamente dous caracteres.";
    }
}
else{  // Pode vir de premer no enlace cando amosa os datos
    $erros[] = "Non enviaches datos.";
}
// Comprobar si hay errores
if (empty($erros)) {
   $formulario_mostrado = false; // No mostrar el formulario si no hay errores
}
?>

<?php
if ($formulario_mostrado) {
    // Mostrar el formulario
    ?>
    <h2>Formulario</h2>
    <form method="POST" action="<?php echo htmlentities($_SERVER["PHP_SELF"]); ?>">
        <label for="nome">Nome:</label>
        <input type="text" id="nome" name="nome" maxlength="20" value="<?php echo $nome; ?>"><br><br>

        <label for="idade">Idade:</label>
        <input type="text" id="idade" name="idade" maxlength="2" size="3" value="<?php echo $idade; ?>"><br><br>

        <input type="submit" name="btnEnviar" value="Enviar Datos"><br><br>
    </form>

<?php
}
if(empty($erros)) {
    // Mostrar os datos enviados nunha lista
    echo "<h2>Datos enviados:</h2>";
    echo "<ul>";
    echo "<li>Nome: $nome</li>";
    echo "<li>Idade: $idade</li>";
    echo "</ul>";
}
else {
    // Mostrar erros
    echo "<h2>Erros:</h2>";
    echo "<ul>";
    foreach ($erros as $erro) {
        echo "<li>$erro</li>";
    }
    echo "</ul>";
}
?>

</body>
</html>



  • Solución Exercicio 2: Fai o necesario para xestionar as operacións de alta, baixa e modifcación dunha lista de froitas. Se debe asegurar que no texto da froita non se poida empregar nun ataque XSS ou de inxección de código. Se ven baleiro o campo de texto debe amosar o erro. Debe amosarse en forma de táboa a lista de froitas engadidas. Ao carón de cada froita debe aparecer un botón cun texto eliminar, que borre dito elemento da lista de froitas. Se non hai elementos na lista debe amosar o texto 'Cesta baleira'. Nun arquivo separado de nome operacionsFroitas, se gardará nunha variable o array de froitas e as funcións para verificar que o nome da froita sexa correcto e cambiar no nome os caracteres non permitidos. Se ven baleiro debe devolver false. Outra función para engadir unha froita ao array e se a froita xa estivera engadidad non facer nada, una función que dada unha froita, a elimine do array se existe. Dito arquivo é necesario para o funcionamento da páxina principal e sabemos que non vai cargarse nada máis que unha vez. Terá de nome operaciónsFroitas.php e estará gardado no cartafol /utilidades/.
Arquivo /utilidades/operacionsFroitas.inc.php
<?php
// Array para gardar as froitas
$listaFroitas = [];

/**
 *  Función para verificar se o nome da froita é válido
 *  Devolve o nome cos caracteres non permitidos escapados ou false en caso de que esta baleiro
 *  @param string nome: nome da froita a chequear
 * */ 
function verificarNomeFroita($nome) {
    // Verificamos que o nome non estea baleiro e que conteña caracteres validos
    $nome = filter_var($nome, FILTER_SANITIZE_STRING);  // Deprecated. Mellor => filter_input(INPUT_POST, 'nome', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES);

    if (empty($nome)) return false;
    else return $nome;
}

/**
 * Función para engadir unha froita á lista
 *  @param string nome: nome da froita
 */ 
function engadirFroita($nomeFroita) {
    global $listaFroitas;
    
    if (!in_array($nomeFroita, $listaFroitas)) {
        $listaFroitas[] = $nomeFroita;
    }
  
}

/**
 *  Función para eliminar unha froita da lista
 *   @param string nome: nome da froita a chequear
 */ 

function eliminarFroita($nomeFroita) {
    global $listaFroitas;
    $indice = array_search($nomeFroita, $listaFroitas);
    if ($indice !== false) {
        unset($listaFroitas[$indice]);
    }
}

/*
* Función para obter a lista de froitas
*/
function obterListaFroitas() {
    global $listaFroitas;
    return $listaFroitas;
}
?>


Arquivo xestionFroitas.php
<!DOCTYPE html>
<html>
<head>
    <title>Xestión de Lista de Froitas</title>
</head>
<body>
    <h1>Xestión de Lista de Froitas</h1>

    <?php
    // Incluímos o arquivo operacionsFroitas.inc.php
    require 'utilidades/operacionsFroitas.inc.php';

    // Xa tiñamos froitas no listado en forma de campos ocultos
    if (isset($_POST['listaFroitas'])) $listaFroitas = $_POST['listaFroitas'];
    if (isset($_POST['btnBaleirar'])) $listaFroitas = array();

    // Comprobamos se se enviou unha nova froita => pulsa botón
    if (isset($_POST['btnEngadir']) && isset($_POST['novaFroita'])) {   // Non verifico empty xa que o fai a función
        $novaFroita = verificarNomeFroita($_POST['novaFroita']);
        // Verificamos se o nome da froita é válido
        if ($novaFroita) {
            engadirFroita($novaFroita);
        } else {
            echo "<p class='erro'>Non introducido o nome da froita.</p>";
        }
    }

    // Comprobamos se se enviou unha froita para eliminar
    if (isset($_POST['btnBorrar'])) {
        $froitaAEliminar = $_POST['btnBorrar'];
        eliminarFroita($froitaAEliminar);
    }

    // Obtemos a lista de froitas
    $listaFroitas = obterListaFroitas();

    // Amosamos a táboa de froitas ou a mensaxe de cesta baleira
    if (empty($listaFroitas)) {
        echo "<p>Cesta baleira.</p>";
    }
    echo "<h2>Operacións Froita</h2>";
    printf('<form method="post" action="%s">',htmlentities($_SERVER['PHP_SELF']));
        echo "<table>";
        echo "<tr><th>Froita</th><th>Acción</th></tr>";
        foreach ($listaFroitas as $key=>$froita) {
            echo "<tr>";
            echo "<td>$froita";
            printf('<input type="hidden" value="%s" name="listaFroitas[%s]" />',$froita,$key);
            echo "</td>";
            printf("<td><button type='submit' id='btnBorrar' name='btnBorrar' value='%s'>Borrar</button></td>",$froita);
            echo "</tr>";
        }
        echo "</table>";
    ?>

        <label for="novaFroita">Nome da Froita:</label>
        <input type="text" id="novaFroita" name="novaFroita">
        <button type="submit" name='btnEngadir' value='engadir'>Engadir Froita</button>
        <?php if(!empty($listaFroitas)) ?>
            <div><button type="submit" name='btnBaleirar' value='baleirar'>Baleirar Cesta</button></div>
            

    </form>
</body>
</html>



  • Solución Exercicio 3: Crea nun arquivo separado de nome /utilidades/usuarios, un array de datos de usuario (cada usuario ten: id, nome, tfno, email). Nese mesmo arquivo define unha función de nome obterUsuarioPorID que en base a un id, busque no array de usuarios, toda a información do usuario con ese id e que devolva toda a información dese usuario se existe. Se non existe debe devolver null. Amosa unha páxina de nome listaUsuarios, unha lista de nomes de usuario, en forma de enlace. Ao premer no nome, deberá amosar toda a información do usuario noutra páxina diferente de nome detalleUsuario . Esa segunda páxina terá un enlace para ir á primeira páxina e volver a listar os usuarios.
Se envías un id que non existe deberá amosar unha mensaxe. Comproba como podes modificar na URL o número de ID a buscar.
Páxina usuarios.inc.php
<?php
// Array de datos de usuarios (id, nome, tfno, email)
$usuarios = [
    ["id" => 1, "nome" => "Usuario 1", "tfno" => "123456789", "email" => "usuario1@example.com"],
    ["id" => 2, "nome" => "Usuario 2", "tfno" => "987654321", "email" => "usuario2@example.com"],
    ["id" => 3, "nome" => "Usuario 3", "tfno" => "555555555", "email" => "usuario3@example.com"]
];

// Función para obter os datos dun usuario a partir do ID
function obterUsuarioPorID($id) {
    global $usuarios;
    foreach ($usuarios as $usuario) {
        if ($usuario["id"] == $id) {
            return $usuario;
        }
    }
    return null; // Devolve null se o usuario non se atopa
}
?>
Páxina listaUsuarios.php
<!DOCTYPE html>
<html>
<head>
    <title>Listado de Usuarios</title>
</head>
<body>
    <h1>Listado de Usuarios</h1>

    <?php
    require_once 'utilidades/usuarios.inc.php';

    // Amosar a lista de usuarios como enlaces
    echo "<ul>";
    foreach ($usuarios as $usuario) {
        echo "<li><a href='detalleUsuario.php?id={$usuario["id"]}'>{$usuario["nome"]}</a></li>";
    }
    echo "</ul>";
    ?>
</body>
</html>


Páxina detalleUsuario.php
<!DOCTYPE html>
<html>
<head>
    <title>Detalles do Usuario</title>
</head>
<body>
    <h1>Detalles do Usuario</h1>

    <?php
    require 'utilidades/usuarios.inc.php';

    // Verificar se se pasou un ID válido por parámetro
    if (isset($_GET["id"])) {
        $idUsuario = htmlentities($_GET["id"]);
        // Chamar á función para obter os datos do usuario
        $usuarioSeleccionado = obterUsuarioPorID($idUsuario);

        // Amosar os detalles do usuario
        if ($usuarioSeleccionado !== null) {
            echo "<p>Nome: {$usuarioSeleccionado["nome"]}</p>";
            echo "<p>Teléfono: {$usuarioSeleccionado["tfno"]}</p>";
            echo "<p>Email: {$usuarioSeleccionado["email"]}</p>";
            echo "<p><a href='listaUsuarios.php'>Volver á lista de usuarios</a></p>";
        } else {
            echo "<p>Usuario non encontrado.</p>";
        }
    } else {
        echo "<p>ID de usuario non especificado.</p>";
    }
    ?>
</body>
</html>



-- Ángel D. Fernández González -- (2016).