Diferencia entre revisiones de «PHP Paso de datos con formularios»

De MediaWiki
Ir a la navegación Ir a la búsqueda
(Se ha deshecho la revisión 24991 de Angelfg (disc.))
Etiqueta: Deshacer
Línea 1: Línea 1:
 
== Introdución ==
 
== Introdución ==
  
−
* Cando programamos pode darse o caso de que teñamos código (en forma de funcións, variables, constantes, clases ou bloques de código) que teñamos que utilizar en moitas páxinas distintas.
+
* 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 [https://www.w3schools.com/html/html_form_elements.asp os indicados neste enlace].
  
−
* Nestes casos é conveniente ter o código gardado nun arquivo separado e poder incorporalo cando o necesitemos.
 
  
−
* En PHP podemos facelo coas construcións da linguaxe: '''include''', '''include_once''', '''require''' e '''require_once'''.
+
* 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:
 +
:::<syntaxhighlight lang="php" line enclose="div" highlight="1" >
 +
<form name="form1" action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST">
  
−
<br />
+
</form>
−
== include ==
+
</syntaxhighlight>
 +
 
 +
 
 +
 
 +
* <u>Nota:</u> Para pasar datos dunha páxina a outra tamén podemos empregar formularios con campos ocultos (hidden).
 +
 
 +
== Método GET ==
 +
 
 +
* Neste caso os datos son enviados na URL e pasan a páxina indicada polo etiqueta '''action''' do formulario no seguinte formato: http://paxinadestino.php?param1=valor&param2=valor&.....
 +
* Se vos fixades cando facedes unha busca en google (por exemplo buscade 'php') na url aparecerá algo coma isto: https://www.google.es/search?q=php&ie=utf-8&oe=utf-8&client=firefox-b&gfe_rd=cr&ei=a1XgV4CmOu6s8wfuibOQAQ
 +
: 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 [http://php.net/manual/es/reserved.variables.get.php $_GET['param']].
 +
 
 +
 
 +
* Vexamos agora un exemplo <u>utilizando un formulario</u>:
  
−
* <u>Sintaxe:</u>
+
'''Arquivo: UD2_Form_Ex1_eleccion_pelicula.php'''
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="27" >
 +
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 +
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 +
<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
 +
    <head>
 +
        <meta charset="utf-8" />
 +
        <title>Formulario</title>
 +
        <style>
 +
            .formularios{
 +
                width: 30%;
 +
                margin: 0px auto;
 +
            }
 +
            .etiqueta{
 +
                float: left;
 +
                width: 190px;
 +
            }
 +
            .botonesformularios{
 +
                width: 50%;
 +
                margin: 30px auto;
 +
                text-align: center;
 +
            }
 +
            select {
 +
                width:50%;
 +
            }
 +
        </style>
 +
    </head>
 +
    <body>
 +
        <form id="frmPelicula" class="formularios" method="get" action="UD2_Form_Ex1_datos_pelicula.php">
 +
            <div>   
 +
                <div class='etiqueta'>Película preferida:</div>
 +
                <select name="lstPelicula"><option>E.T. El extraterrestre</option><option>Supermán</option></select>
 +
            </div>               
 +
            <div>
 +
                <div class='etiqueta'>Xénero preferido:</div>
 +
                <select name="lstXenero"><option>Acción</option><option>Ciencia ficción</option></select>
 +
            </div>               
 +
            <div class="botonesformularios">
 +
                <input type='submit' value="ENVIAR" />
 +
            </div>
 +
        </form>
  
−
::<syntaxhighlight lang="php" enclose="div">
+
    </body>   
−
include '[path/]arquivo.php';
+
</html>
 +
</php>
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
* A sentenza '''include''' inclúe e avalía o arquivo especificado.
+
:* Fixarse que enviamos os datos utilizando o '''método get'''.
  
−
* O camiño pode ser absoluto ou relativo. Para evitar problemas coa resolución de rutas é recomendable construír a ruta tomando como referencia o directorio do arquivo PHP actual mediante a constante máxica '''__DIR__'''.
+
'''Arquivo: UD2_Form_Ex1_datos_pelicula.php'''
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="13,14" >
 +
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 +
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  
−
: Por exemplo:
+
<html>
 +
    <head>
 +
        <meta charset="UTF-8">
 +
        <title></title>
 +
    </head>
 +
    <body>
 +
        <?php
 +
        echo 'Os datos que escolliches son:<br/><br/>';
 +
       
 +
        $pelicula = $_GET['lstPelicula'];
 +
        $xenero = $_GET['lstXenero'];
  
−
::<syntaxhighlight lang="php" enclose="div">
+
        printf("PELICULA:%s<br/>XÉNERO:%s",$pelicula,$xenero);
−
include __DIR__ . '/utilidades/arquivo.php';
+
        ?>
 +
       
 +
    </body>
 +
</html>
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
* Antigamente empregábase con frecuencia a extensión ''.inc.php'' para este tipo de arquivos, pero actualmente podemos empregar directamente a extensión ''.php''.
 
  
−
* O código PHP do arquivo incluído debe atoparse entre etiquetas PHP.
 
  
−
: Se o arquivo contén unicamente código PHP, é recomendable empregar a etiqueta de apertura '''<?php''' e '''omitir a etiqueta de peche ?>''' ao final do arquivo.
+
* <u>NOTA:</u> Se queredes visualizar o conxunto de valores que temos en $_GET podedes empregar [https://www.php.net/manual/es/function.var-dump.php 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.
  
−
: Desta forma evitamos que espazos ou saltos de liña situados accidentalmente despois da etiqueta de peche produzan unha saída non desexada.
+
* 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 [http://php.net/manual/es/ini.core.php#ini.post-max-size 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).
  
−
* Vexamos un exemplo:
+
: <u>Nota:</u> No caso de subir arquivos tamén pode ser necesario modificar a [http://php.net/manual/es/ini.core.php#ini.upload-max-filesize directiva upload_max_filesize] do arquivo php.ini
 +
: Podedes ver [http://angelfernandezgonzalez.blogspot.com.es/2015/10/subir-archivos-de-mas-de-8mb-en.html neste enlace] como aumentar o tamaño dos arquivos subidos nun aloxamento compartido.
  
−
: '''Arquivo: FuncionsExternas.php'''
+
 +
* As variables que se pasan desta forma poden ser referenciadas en PHP a través da matriz global [http://php.net/manual/es/reserved.variables.post.php $_POST['param']].
  
−
::<syntaxhighlight lang="php" line enclose="div">
 
−
<?php
 
  
−
/**
+
* Modificamos o exemplo anterior para que envíe os datos con POST:
−
* Crea un array cun número específico de elementos inicializados a null.
 
−
*
 
−
* @param int $numElementos Número de elementos que terá o array.
 
−
*
 
−
* @return array Array co número de elementos indicado, todos con valor null.
 
−
*/
 
−
function crearArray(int $numElementos): array
 
−
{
 
−
    $array = [];
 
  
−
     for ($cont = 0; $cont < $numElementos; $cont++) {
+
'''Arquivo: UD2_Form_Ex2_eleccion_pelicula.php'''
−
         $array[$cont] = null;
+
::<syntaxhighlight lang="php" line enclose="div" highlight="27" >
−
    }
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 +
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 +
<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
 +
     <head>
 +
        <meta charset="utf-8" />
 +
        <title>Formulario</title>
 +
        <style>
 +
            .formularios{
 +
                width: 30%;
 +
                margin: 0px auto;
 +
            }
 +
            .etiqueta{
 +
                float: left;
 +
                width: 190px;
 +
            }
 +
            .botonesformularios{
 +
                width: 50%;
 +
                margin: 30px auto;
 +
                text-align: center;
 +
            }
 +
            select {
 +
                width:50%;
 +
            }
 +
         </style>
 +
    </head>
 +
    <body>
 +
        <form id="frmPelicula" class="formularios" method="post" action="UD2_Form_Ex2_datos_pelicula.php">
 +
            <div>   
 +
                <div class='etiqueta'>Película preferida:</div>
 +
                <select name="lstPelicula"><option>E.T. El extraterrestre</option><option>Supermán</option></select>
 +
            </div>               
 +
            <div>
 +
                <div class='etiqueta'>Xénero preferido:</div>
 +
                <select name="lstXenero"><option>Acción</option><option>Ciencia ficción</option></select>
 +
            </div>               
 +
            <div class="botonesformularios">
 +
                <input type='submit' value="ENVIAR" />
 +
            </div>
 +
        </form>
  
−
     return $array;
+
     </body>   
−
}
+
</html>
 +
</php>
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
: '''Arquivo: ReutilizandoCodigo.php'''
+
:* Fixarse que enviamos os datos utilizando o '''método post'''.
  
−
::<syntaxhighlight lang="php" line enclose="div">
+
'''Arquivo: UD2_Form_Ex2_datos_pelicula.php'''
−
<!DOCTYPE html>
+
::<syntaxhighlight lang="php" line enclose="div" highlight="13,14" >
−
<html lang="gl">
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
−
<head>
+
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
−
     <meta charset="UTF-8">
 
−
    <title>Exemplos de código PHP</title>
 
−
</head>
 
−
<body>
 
−
<?php
 
−
include __DIR__ . '/FuncionsExternas.php';
 
  
−
$datos = crearArray(10);
+
<html>
 +
    <head>
 +
        <meta charset="UTF-8">
 +
        <title></title>
 +
    </head>
 +
    <body>
 +
        <?php
 +
        echo 'Os datos que escolliches son:<br/><br/>';
 +
       
 +
        $pelicula = $_POST['lstPelicula'];
 +
        $xenero = $_POST['lstXenero'];
  
−
foreach ($datos as $clave => $dato) {
+
        printf("PELICULA:%s<br/>XÉNERO:%s",$pelicula,$xenero);
−
    printf(
+
        ?>
−
        "Clave:%d - Valor:%s<br>",
+
       
−
        $clave,
+
    </body>
−
        $dato === null ? 'Sen dato' : $dato
 
−
    );
 
−
}
 
−
?>
 
−
</body>
 
 
</html>
 
</html>
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
* As funcións e clases definidas nun arquivo incluído teñen ámbito global.
 
  
−
* No caso das variables, o arquivo incluído herda o ámbito do punto no que se realiza a inclusión.
 
  
−
: Por exemplo, se facemos un '''include''' dentro dunha función, as variables definidas no arquivo incluído pertencerán ao ámbito desa función.
 
  
−
* Cando fagamos unha aplicación con acceso a base de datos teremos que gardar información sensible, como datos de conexión.
+
* <u>NOTA:</u> Se queredes visualizar o conxunto de valores que temos en $_POST podedes empregar [https://www.php.net/manual/es/function.var-dump.php 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 [https://wiki.cifprodolfoucha.es/index.php?title=PHP_Constantes_e_variables._%C3%81mbito.#Comprobaci.C3.B3n_de_definici.C3.B3n_e_asignaci.C3.B3n as funcións que comproban se un campo ten datos] e [https://wiki.cifprodolfoucha.es/index.php?title=PHP_Constantes_e_variables._%C3%81mbito.#Comprobaci.C3.B3n_do_tipo_de_dato as funcións que comproban o tipo de dato dunha variable].
 +
 
 +
 
 +
 
 +
* O primeiro que teríamos que comprobar é se o formulario recibe datos. Para iso utilizaremos as funcións vistas [https://wiki.cifprodolfoucha.es/index.php?title=PHP_Constantes_e_variables._%C3%81mbito.#Comprobaci.C3.B3n_de_definici.C3.B3n_e_asignaci.C3.B3n nunha sección anterior da wiki].
 +
 
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
  
−
: É recomendable gardar este tipo de información nun arquivo situado '''fóra''' da ruta pública do servidor web.
+
if (empty($_GET)) die('Non se enviaron datos');  // Isto é válido para action=get no formulario
 +
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
  
−
:: Imaxinemos que o servidor web publica por defecto o cartafol:
+
</syntaxhighlight>
  
−
::<syntaxhighlight lang="text">
+
<u>Nota:</u> A [http://www.w3schools.com/php/func_misc_die.asp función die] ao igual que a [http://php.net/manual/es/function.exit.php función exit] finaliza a páxina php.
−
/var/www/html
+
 
 +
: 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.
 +
 
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
if (empty($_POST['txtNome'])) echo('Campo nome non ten datos');
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
:: Poderiamos gardar a configuración, por exemplo, en:
+
* <u>'''IMPORTANTE:'''</u> 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:
  
−
::<syntaxhighlight lang="text">
+
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
−
/var/www/configuracions/informacionbd.php
+
if (ctype_digit($_POST['txtIdade'])){
 +
    $idade = (int)$_POST['txtIdade'];
 +
}
 +
else {
 +
    echo 'Non é numérico integer';
 +
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
:: Desta forma, coa configuración habitual do servidor web, ese arquivo non poderá solicitarse directamente mediante unha URL.
+
: Usamos a [http://php.net/manual/es/function.ctype-digit.php 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"),
 +
 
 +
 
 +
 
 +
 
  
 
<br />
 
<br />
−
== include_once ==
+
* '''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 que fai dita validación chámase [http://php.net/manual/es/function.filter-var.php función filter_var()]
 +
 
 +
: 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 [http://php.net/manual/es/function.addslashes.php 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 &amp;
 +
:: 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
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
<?php 
 +
    $var = 123; 
 +
    echo filter_var($var, FILTER_VALIDATE_INT); 
 +
?>
 +
</syntaxhighlight>
 +
:: Devolve o número xa que é un enteiro.
 +
 
 +
* Validando un número enteiro
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
<?php 
 +
    $var = -2.343; 
 +
    if(filter_var($var, FILTER_VALIDATE_INT) === false){ 
 +
        echo 'Valor incorrecto'; 
 +
    }else{ 
 +
        echo 'Valor correcto'; 
 +
    } 
 +
?>
 +
</syntaxhighlight>
 +
:: Devolve 'Valor incorrecto' xa que non é un enteiro e polo tanto a función devolve false.
 +
 
 +
* Validando un número enteiro
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
<?php 
 +
    $var = -2; 
 +
    if(filter_var($var, FILTER_VALIDATE_INT) === false){ 
 +
        echo 'Valor incorrecto'; 
 +
    }else{ 
 +
        echo 'Valor correcto'; 
 +
    } 
 +
?>
 +
</syntaxhighlight>
 +
:: Devolve 'Valor correcto'.
 +
 
 +
 
 +
* Validando un número flotante
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="2,3" >
 +
<?php
 +
    $var = '-1.45'; 
 +
    if(filter_var($var, FILTER_VALIDATE_FLOAT) === false){ 
 +
        echo 'Valor incorrecto'; 
 +
    }else{ 
 +
        echo 'Valor correcto'; 
 +
    } 
 +
?>
 +
</syntaxhighlight>
 +
:: Devolve 'Valor correcto'.
 +
 
 +
 
 +
* Validando un email
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
<?php 
 +
    $var = 'casa@hotmail'; 
 +
    if (!filter_var($var,FILTER_VALIDATE_EMAIL)){
 +
        echo "Valor incorrecto";
 +
    }else{ 
 +
        echo 'Valor correcto'; 
 +
    } 
 +
?>
 +
</syntaxhighlight>
 +
:: 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.
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="3" >
 +
<?php 
 +
    $var = '<isto non está permitido> Isto si aparece'; 
 +
    echo filter_var($var, FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES);
 +
</syntaxhighlight>
 +
:: Devolve 'Isto si aparece'.
 +
 
 +
 
 +
* Corrixindo un número flotante.
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
<?php
 +
$number="53.3pp";
 +
 
 +
var_dump(filter_var($number, FILTER_SANITIZE_NUMBER_FLOAT,
 +
FILTER_FLAG_ALLOW_FRACTION));  // Este flag fai que o caracter 'punto' sexa válido (separador de decimais)
 +
?>
 +
</syntaxhighlight>
 +
:: Devolve '53.3'.
 +
 
 +
=== Establecendo criterios ===
 +
 
 +
* Podemos indicar que rango de valores son aceptables polo filtro.
 +
 
 +
* Validando un número enteiro dentro dun rango
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="5,6" >
 +
<?php 
 +
    $var = -200; 
 +
    $opciones = array(
 +
        'options' => array(
 +
        'min_range' => 10,
 +
        'max_range' => 100
 +
    ));
 +
     
 +
    if(filter_var($var, FILTER_VALIDATE_INT, $opciones) === false){ 
 +
        echo 'Valor incorrecto'; 
 +
    }else{ 
 +
        echo 'Valor correcto'; 
 +
    } 
 +
?>
 +
</syntaxhighlight>
 +
:: Devolve 'Valor incorrecto'
 +
 
 +
 
 +
* Podemos facer unha modificación para que en caso incorrecto devolva sempre un valor 'por defecto':
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="5" >
 +
<?php 
 +
    $var = -200; 
 +
    $opciones = array(
 +
        'options' => array(
 +
        'default' => 3, // valor a retornar se o filtro faiaa
 +
        'min_range' => 10,
 +
        'max_range' => 100
 +
    ));
 +
     
 +
    $var = filter_var($var, FILTER_VALIDATE_INT, $opciones);
 +
    echo $var;
 +
 
 +
?>
 +
</syntaxhighlight>
 +
:: 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.
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="2,5" >
 +
<?php 
 +
    $var = '1,23'; 
 +
    $opciones = array(
 +
        'options' => array(
 +
        'decimal' => ','
 +
    ));
 +
     
 +
    if(filter_var($var, FILTER_VALIDATE_FLOAT, $opciones) === false){ 
 +
        echo 'Valor incorrecto'; 
 +
    }else{ 
 +
        echo 'Valor correcto'; 
 +
    } 
 +
?>   
 +
 
 +
</syntaxhighlight>
 +
:: Devolve 'Valor correcto'
 +
 
 +
 
 +
*<u>Nota:</u> Podemos facer uso da [http://php.net/manual/en/function.filter-input.php 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'
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="2,5" >
 +
<?php
 +
            $nome=$_POST['usuarios'];
 +
            $password=$_POST['password'];
 +
                     
 +
            printf("select count(*) from USUARIOS where nome='%s' and password='%s'",$nome,$password);
 +
?>
 +
</syntaxhighlight>
 +
: 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 <b>nome='angel'</b> and <b>password='12345678'</b>
 +
 
 +
* Agora <u>modifiquemos o campo password</u> 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.
 +
 
 +
<u>Nota:</u> 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:
 +
 
 +
::<syntaxhighlight lang="java" line enclose="div" highlight="" >
 +
DELIMITER //
 +
CREATE PROCEDURE country_hos
 +
(IN log VARCHAR(20),IN pass VARCHAR(30))
 +
BEGIN
 +
  SELECT count(*) FROM USUARIOS
 +
  WHERE login = log and password = pass;
 +
END //
 +
DELIMITER ;
 +
</syntaxhighlight>
 +
 
 +
: 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 que temos é a de utilizar os [http://wiki.cifprodolfoucha.es/index.php?title=PHP_Paso_de_datos_con_formularios#Filtros_para_a_correcci.C3.B3n filtros de corrección vistos anteriormente] como por exemplo FILTER_SANITIZE_FULL_SPECIAL_CHARS que fai un escape dos caracteres como as cominllas simples ou dobres.
 +
 
 +
* Outra opción é a de utilizar funcións que provea o S.X.B.D. utilizado.
 +
: No caso de mysql temos a [http://php.net/manual/es/mysqli.real-escape-string.php función mysqli_real_escape_string]. O veremos cando cheguemos á sección de acceso a datos.
 +
 
 +
 
 +
 
 +
* Tamén podemos empregar a [http://php.net/manual/es/mysqli.prepare.php función mysqli::prepare] como [http://wiki.cifprodolfoucha.es/index.php?title=PHP_Operaci%C3%B3ns_BD_Mysql#Optimizaci.C3.B3n:_Sentenzas_Prepared veremos posteriormente en acceso a datos].
 +
 
 +
=== Cross-Site Scripting (XSS) ===
 +
 
 +
* Máis información [https://es.wikipedia.org/wiki/Cross-site_scripting neste enlace].
 +
 
 +
 
 +
* 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 <u>vixiar os datos que VISUALICEMOS na páxina</u>.
 +
 
  
−
* <u>Sintaxe:</u>
+
: Un exemplo sería se introducimos dentro dun text-area este código:
 +
::<syntaxhighlight lang="java" line enclose="div" highlight="" >
 +
<script>window.location="http://www.google.es";</script>
 +
</syntaxhighlight>  
  
−
::<syntaxhighlight lang="php" enclose="div">
+
: O que vai pasar é que o navegador cando cargue a páxina vai cargar a páxina de google indicada no código javascript.
−
include_once '[path/]arquivo.php';
+
: 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...
 +
 
 +
<u>Importante:</u> 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:
 +
 
 +
::<syntaxhighlight lang="java" line enclose="div" highlight="" >
 +
<form method="post" action="<?php echo $_SERVER['PHP_SELF']); ?>" name="formulario_perfumes">
 +
 
 +
......
 +
 
 +
</form>
 +
</syntaxhighlight>
 +
 
 +
 
 +
==== Solucións ao ataque Cross-Site Scripting (XSS) ====
 +
 
 +
 
 +
* Igual que no caso anterior podemos utilizar a [http://wiki.cifprodolfoucha.es/index.php?title=PHP_Paso_de_datos_con_formularios#Funci.C3.B3n_para_validar_.2F_corrixir_campos función filter_var] cos filtros FILTER_SANITIZE_MAGIC_QUOTES ou FILTER_SANITIZE_FULL_SPECIAL_CHARS para eliminar dita posibilidade.
 +
 
 +
* En PHP podemos facer uso das seguintes funcións:
 +
:* [http://php.net/manual/es/function.strip-tags.php Función strip_tags]: Dita función fai o mesmo que FILTER_SANITIZE_FULL_SPECIAL_CHARS e ademais podemos indicar cales etiquetas son permitidas.
 +
:* [http://php.net/manual/es/function.htmlentities.php 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)
 +
:* [https://www.php.net/manual/es/function.htmlspecialchars.php htmlspecialchars]: Moi parecida á anterior, ten a mesma función pero é máis lixeira a nivel de carga de CPU xa que elimina soamente aqueles caracteres que poidan ser empregados para un ataques XSS). Normalmente a empregaremos con estes argumentos: $novoDato = htmlspecialchars($dato, ENT_QUOTES, 'UTF-8');  
 +
:: É a opción a empregar cando queramos visualizar datos que proveñan da base de datos ou dun campo de formulario.
 +
 
 +
 
 +
: No caso do formulario poderíamos poñer:
 +
<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES, 'UTF-8') ?>" name="formulario_perfumes">
 +
 
 +
......
 +
 
 +
</form>
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
* '''include_once''' ten o mesmo comportamento que '''include''', pero PHP comproba se o arquivo xa foi incluído previamente. Se xa foi incluído, non o volve procesar.
 
  
−
* Isto é especialmente útil cando o arquivo define funcións, clases ou constantes, xa que incluílo máis dunha vez podería provocar erros por declaracións duplicadas.
+
<br />
 +
 
 +
===Ataques CSRF (Cross-Site Request Forgery)===
 +
 
 +
* O veremos [https://wiki.cifprodolfoucha.es/index.php?title=PHP_Sesi%C3%B3ns#Ataques_CSRF_.28Cross-Site_Request_Forgery.29 cando vexamos as variables de sesión na UD4].
 +
 
 +
 
 +
 
 +
<br />
 +
===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 mysqli_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.
 +
 
 +
 
 +
 
 +
 
 +
 
 +
<br />
 +
 
 +
== 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 é [http://php.net/manual/es/function.filter-input.php filter_input].
 +
 
 +
* Sintaxe:
 +
::<syntaxhighlight lang="java" line enclose="div" highlight="" >
 +
mixed filter_input ( int $type , string $variable_name [, int $filter = FILTER_DEFAULT [, mixed $options ]] )
 +
</syntaxhighlight>
 +
 
 +
: 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:
 +
::<syntaxhighlight lang="java" line enclose="div" highlight="" >
 +
 
 +
<?php
 +
$buscar_html = filter_input(INPUT_GET, 'buscar', FILTER_SANITIZE_SPECIAL_CHARS);
 +
$buscar_url = filter_input(INPUT_GET, 'buscar', FILTER_SANITIZE_ENCODED);
 +
echo "Has buscado $buscar_html.\n";
 +
echo "<a href='?buscar=$buscar_url'>Buscar de nuevo</a>";
 +
?>
 +
 
 +
</syntaxhighlight>
 +
 
 +
* Información obtida de [http://php.net/manual/es/function.filter-input.php php.net].
 +
 
 +
 
 +
== 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.
 +
 
 +
* Para facelo podemos usar a [http://php.net/manual/es/function.header.php función header] de PHP.
 +
 
 +
: 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 é:
 +
 
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
<?php
 +
header('Location: http://www.nova_paxina.es/');
 +
exit;
 +
?>
 +
</syntaxhighlight>
 +
 
 +
:<u><b>Importante:</b></u>
 +
:* É NECESARIO QUE A CHAMADA Á FUNCIÓN header SE FAGA <u>ANTES</u> 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 <u>antes</u> da orde header.
 +
:* Si existe código de script posteriormente á chamada do header será necesario poñer '''exit;''' despois da orde header para que non sigua a executar o script.
 +
 
  
−
* Debemos escoller entre '''include''' e '''include_once''' en función de se queremos permitir ou non que o mesmo arquivo sexa procesado máis dunha vez.
+
:: Tamén indicar que como veremos posteriormente cando enviamos datos con get, estes datos aparecen na páxina en forma de paráemtros &param1=valor1&param2=valor2&...polo tanto podemos facer unha redirección enviando datos na URL da forma:
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
<?php
 +
header('Location: http://www.nova_paxina.es?parametro1=valor1&paramentro2=valor2');
 +
?>
 +
</syntaxhighlight>
 +
: E na seguinte páxina obter os datos con $_GET['parametro1'],...
  
−
* Pode darse o caso, por exemplo, cando facemos inclusións condicionais dentro dun '''if''', de que un arquivo xa fose cargado previamente.
 
  
−
* Os arquivos incluídos tamén poden utilizarse para gardar elementos comúns a varias páxinas web, como unha cabeceira, un menú ou un pé de páxina.
 
  
−
: Desta forma, se necesitamos modificar un elemento común, soamente teremos que facelo nun único arquivo.
 
  
−
* En PHP existe outra forma de organizar os elementos comúns empregando un modelo ou patrón MVC.
 
  
−
: Podedes consultalo [http://wiki.cifprodolfoucha.es/index.php?title=PHP_Conceptos_b%C3%A1sicos_Modelo-Vista-Controlador#Opci.C3.B3n_2 neste enlace da WIKI].
 
  
 
<br />
 
<br />
−
== require ==
+
* Un complemento interesante para visualizar a cabeceira dunha páxina: https://addons.mozilla.org/es/firefox/addon/live-http-headers/
 +
: Ao instalalo teremos a posibilidade de capturar os datos de cabeceira da páxina se imos ao menú '''Herramientas''' => '''Live HTTP Headers'''.
 +
 
 +
<gallery caption="Complemento Live HTTP Headers" widths="350" heights="300px" perrow="2">
 +
Image:Php_paso_datos_1.JPG| Unha vez instalado aparece unha nova opción de menú en Herramientas.
 +
Image:Php_paso_datos_2.JPG| Coa ventá aberta, se cargamos unha páxina aparecen os datos enviados como cabeceira ao navegador web dende o servidor.
 +
</gallery>
 +
 
 +
: Coa [http://php.net/manual/es/function.header.php 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 [http://wiki.cifprodolfoucha.es/index.php?title=PHP_Instalaci%C3%B3n_Apache 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 [http://wiki.cifprodolfoucha.es/index.php?title=DRUPAL_Instalaci%C3%B3n#Arquivos_de_configuraci.C3.B3n 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 [http://wiki.cifprodolfoucha.es/index.php?title=DRUPAL_Instalaci%C3%B3n#Arquivos_de_configuraci.C3.B3n 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
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
cd /var/www/html
 +
mkdir IMAXES
 +
chown www-data:www-data IMAXES
 +
chmod 755 IMAXES
 +
</syntaxhighlight>
 +
 
 +
 
  
−
* <u>Sintaxe:</u>
+
: 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
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="10,11" >
 +
<IfModule unixd_module>
 +
#
 +
# If you wish httpd to run as a different user or group, you must run
 +
# httpd as root initially and it will switch.
 +
#
 +
# User/Group: The name (or #number) of the user/group to run httpd as.
 +
# It is usually good practice to create a dedicated user and group for
 +
# running httpd, as with most system services.
 +
#
 +
User daemon
 +
Group daemon
 +
</IfModule>
 +
</syntaxhighlight>  
  
−
::<syntaxhighlight lang="php" enclose="div">
+
:: Nas liñas 10 e 11 se indica o usuario e grupo que utiliza o Apache de XAMPP para acceder aos cartafoles.
−
require '[path/]arquivo.php';
+
:: Deberedes cambiar a liña do script anterior de '''chown www-data:www-data IMAXES''' a '''chown daemon:daemon IMAXES'''
−
</syntaxhighlight>
 
  
−
* '''require''' fai o mesmo que '''include''', pero existe unha diferenza importante cando non se pode cargar o arquivo:
 
  
−
:* '''include''' produce un aviso de tipo '''E_WARNING''' e a execución do script pode continuar.
 
−
:* '''require''' produce un erro de tipo '''Error'''. Se non se trata, a execución do script interrómpese.
 
  
−
* Por este motivo empregaremos normalmente '''require''' cando o arquivo sexa imprescindible para que a aplicación poida funcionar.
 
  
−
=== Exemplo con include ===
 
  
−
::<syntaxhighlight lang="php" line enclose="div">
+
'''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 [http://www.w3schools.com/php/php_file_upload.asp www3schools].
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="5,7" >
 
<!DOCTYPE html>
 
<!DOCTYPE html>
−
<html lang="gl">
+
<html>
−
<head>
 
−
    <meta charset="UTF-8">
 
−
    <title>Exemplo con include</title>
 
−
</head>
 
 
<body>
 
<body>
−
<?php
 
−
include __DIR__ . '/FuncionsExternasNonExiste.php';
 
  
−
echo 'Isto aparece porque include permite continuar a execución.';
+
<form action="GardaCampos.php" method="post" enctype="multipart/form-data">
−
?>
+
    Selecciona unha imaxe para subir:
 +
    <input type="file" name="fileToUpload" id="fileToUpload">
 +
    <input type="submit" value="Subir Imaxe" name="btnSubirImaxe">
 +
</form>
 +
 
 
</body>
 
</body>
 
</html>
 
</html>
−
</syntaxhighlight>
+
</syntaxhighlight>  
 +
 
 +
'''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.
  
−
: Se o arquivo non existe, aparecerá un warning, pero a execución poderá continuar.
 
  
−
=== Exemplo con require ===
+
'''Páxina que procesa os datos'''
 +
:Pode ser a mesma do formulario ou outra diferente. No exemplo se chama 'GardaCampos.php'.
  
−
::<syntaxhighlight lang="php" line enclose="div">
+
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
−
<!DOCTYPE html>
 
−
<html lang="gl">
 
−
<head>
 
−
    <meta charset="UTF-8">
 
−
    <title>Exemplo con require</title>
 
−
</head>
 
−
<body>
 
 
<?php
 
<?php
−
require __DIR__ . '/FuncionsExternasNonExiste.php';
+
    // Enviamos datos
 +
if(isset($_POST["btnSubirImaxe"])) {
 +
    if(is_uploaded_file($_FILES["fileToUpload"]["tmp_name"])){  // Subido ao cartafol temporal do servidor
 +
        $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.
 +
 
 +
        $tipo = $_FILES["fileToUpload"]["type"];
 +
        if ($tipo=='image/jpeg')  {  // Neste exemplo verficamos que o arquivo sexa unha imaxe jpg
 +
            if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
 +
                echo "O arquivo ". basename( $_FILES["fileToUpload"]["name"]). " foi subido ao servidor.";
 +
            } else {
 +
                printf("Houbo un erro subindo o arquivo:%s",$_FILES['fileToUpload']['error']);
 +
            }
 +
        }
 +
        else{
 +
            echo ("O tipo de arquivo non é jpg");
 +
        }
 +
    }
 +
    else{
 +
        printf("Houbo un erro subindo o arquivo:%s",$_FILES['fileToUpload']['error']);
 +
    }
 +
 
  
−
echo 'Esta instrución non chegará a executarse se o erro non é tratado.';
+
}
 
?>
 
?>
−
</body>
+
</syntaxhighlight>  
−
</html>
+
* En caso de erro, podemos ver o valor de $_FILES['fileToUpload']['error']:
−
</syntaxhighlight>
+
:* 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.
  
−
: Neste caso, ao ser o arquivo obrigatorio, a execución interrómpese se non pode cargarse e o erro non é tratado.
 
  
−
<br />
+
: <u>'''Nota importante:'''</u> Algunhas veces ['error'] sempre devolve false (0) e non se establece correctamente o error.
−
== require_once ==
+
: Para saber que pasa, temporalmente podemos poñer ao principio da páxina:
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
    error_reporting(E_ALL);
 +
    ini_set('display_errors', 1);
 +
</syntaxhighlight>
  
−
* <u>Sintaxe:</u>
 
  
−
::<syntaxhighlight lang="php" enclose="div">
 
−
require_once '[path/]arquivo.php';
 
−
</syntaxhighlight>
 
  
−
* Ten o mesmo comportamento que '''require''', pero se o arquivo xa foi incluído previamente non se volverá incluír.
+
* ALgunhas función que pode ser de utilidade:
 +
:* [http://php.net/manual/es/function.basename.php 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.  
  
−
* É especialmente útil cando:
 
  
−
:* o arquivo é obrigatorio para o funcionamento da aplicación;
+
:* A [http://php.net/manual/es/function.pathinfo.php función pathinfo] garda a extensión do arquivo a subir.
−
:* non sabemos con certeza se xa foi cargado previamente;
+
:: Por exemplo:
−
:* o arquivo define funcións, clases ou constantes que non deben declararse máis dunha vez.
+
:::$imageFileType = pathinfo($target_file,PATHINFO_EXTENSION);
  
−
: Resumo:
+
:* A [http://php.net/manual/es/function.getimagesize.php 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.";
 +
:::    }
  
−
::{| class="wikitable"
 
−
! Sentenza
 
−
! O arquivo é obrigatorio
 
−
! Evita incluílo máis dunha vez
 
−
|-
 
−
| include
 
−
| Non
 
−
| Non
 
−
|-
 
−
| include_once
 
−
| Non
 
−
| Si
 
−
|-
 
−
| require
 
−
| Si
 
−
| Non
 
−
|-
 
−
| require_once
 
−
| Si
 
−
| Si
 
−
|}
 
  
−
<br />
 
−
== Configuración da visualización e rexistro de erros ==
 
  
−
* Durante o desenvolvemento é importante detectar e corrixir erros, warnings e mensaxes de depreciación.
+
* 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.
  
−
* Nun servidor de produción, pola contra, non debemos amosar directamente os erros ao usuario, xa que poden revelar información interna da aplicación ou do servidor.
 
  
−
=== Entorno de desenvolvemento ===
 
  
−
* Durante o desenvolvemento podemos pedir a PHP que informe de todos os erros:
+
* '''Verificar se o arquivo existe antes de subilo''':
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
<?php
 +
if (file_exists($target_file)) {
 +
    echo "Ese arquivo xa existe.";
 +
    $uploadOk = 0;
 +
}
 +
?>
 +
</syntaxhighlight>
  
−
::<syntaxhighlight lang="php" enclose="div">
 
−
error_reporting(E_ALL);
 
−
ini_set('display_errors', '1');
 
−
</syntaxhighlight>
 
  
−
* '''E_ALL''' indica que queremos recibir todos os erros, warnings, notices e mensaxes de depreciación configuradas por PHP.
 
  
−
=== Entorno de produción ===
+
* '''Limitar o tipo do arquivo''':
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
<?php
 +
$imageFileType = $_FILES["fileToUpload"]["type"];
 +
if($imageFileType != "image/jpeg" && $imageFileType != "/image/png" && $imageFileType != "image/jpg"
 +
&& $imageFileType != "image/gif" ) {
 +
    echo "Soamente arquivos JPG, JPEG, PNG & GIF están permitidos.";
 +
    $uploadOk = 0;
 +
}
 +
?>
 +
</syntaxhighlight>
  
−
* Nun entorno de produción é recomendable non amosar os erros ao usuario e gardalos no rexistro do servidor.
 
  
−
: Unha configuración típica no arquivo ''php.ini'' sería:
 
  
−
::<syntaxhighlight lang="ini" enclose="div">
+
* '''Limitar o tamaño do arquivo''':
−
display_errors = Off
+
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
−
log_errors = On
+
<?php
−
error_reporting = E_ALL
+
// Check file size
−
</syntaxhighlight>
+
if ($_FILES["fileToUpload"]["size"] > 500000) {
 +
    echo "O arquivo ten máis de 500KB. Non se pode subir";
 +
    $uploadOk = 0;
 +
}
 +
?>
 +
</syntaxhighlight>  
  
−
* Desta forma os erros seguirán rexistrándose, pero non se amosarán directamente na páxina web.
+
:* Para o tamaño do arquivo podemos empregar outra aproximación, que consiste en poñer un campo oculto <u>antes do campo de tipo file</u> da seguinte forma:
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="3" >
 +
<form action="GardaCampos.php" method="post" enctype="multipart/form-data">
 +
    Selecciona unha imaxe para subir:
 +
    <input type="hidden" name="MAX_FILE_SIZE" value="10000" /> 
 +
    <input type="file" name="fileToUpload" id="fileToUpload">
 +
    <input type="submit" value="Subir Imaxe" name="btnSubirImaxe">
 +
</form>
 +
</syntaxhighlight>
 +
: O nome ten que ser MAX_FILE_SIZE e o value é o tamaño máximo en bytes.
  
−
=== Máscaras de erros ===
+
:: Cando se procesa o formulario:
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="5,6" >
 +
<?php
 +
if(isset($_POST["btnSubirImaxe"])) {
 +
    $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.
 +
    if ($_FILES['fileToUpload']['error']>0)
 +
        die("Houbo un erro subindo o arquivo: " . $_FILES['fileToUpload']['error']);  // Neste exemplo dará de erro o valor 2 => UPLOAD_ERR_FORM_SIZE (visto anteriormente)
  
−
* PHP tamén permite combinar constantes de erros mediante operadores a nivel de bits.
+
    ......
  
−
: Por exemplo:
+
?>
 +
</syntaxhighlight>
  
−
::<syntaxhighlight lang="php" enclose="div">
 
−
// Erros, warnings e erros de análise.
 
−
error_reporting(E_ERROR | E_WARNING | E_PARSE);
 
  
−
// Todos os erros excepto E_NOTICE.
 
−
error_reporting(E_ALL & ~E_NOTICE);
 
−
</syntaxhighlight>
 
  
−
: Aínda que esta posibilidade existe, durante o desenvolvemento empregaremos normalmente:
+
: 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;'''
  
−
::<syntaxhighlight lang="php" enclose="div">
+
: 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.
−
error_reporting(E_ALL);
+
: Lembrar que o arquivo de configuración de PHP se atopa en:
−
</syntaxhighlight>
+
:* /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.
  
−
=== A través do arquivo .htaccess ===
+
: Se queredes aumentar o tamaño deberedes cambiar ambas directivas aos valores desexados.
  
−
* Se PHP se executa como módulo de Apache e a configuración do servidor o permite, é posible modificar determinadas opcións de PHP mediante o arquivo '''.htaccess'''.
+
: Tedes [http://angelfernandezgonzalez.blogspot.com.es/2015/10/subir-archivos-de-mas-de-8mb-en.html 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.
  
−
: Por exemplo:
 
  
−
::<syntaxhighlight lang="apache" enclose="div">
+
:: 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í).
−
php_value error_reporting 2147483647
+
::* Opción a)
−
php_flag display_errors On
+
:::: Crear un arquivo '''php.ini''' no cartafol raíz do noso sitio web e poñer:
−
</syntaxhighlight>
+
:::::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
upload_max_filesize = 64M
 +
post_max_size = 64M
 +
max_execution_time = 300
 +
</syntaxhighlight>  
 +
::::: No exemplo estamos a limitar en 64MB o tamaño da subida e o tempo de execución en 5 minutos.
  
−
* Esta forma de configuración '''non é válida para todas as formas de executar PHP'''.
+
::* Opción b)
 +
:::: Crear un arquivo '''.htaccess''' no cartafol raíz do noso sitio web e poñer:
 +
:::::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
php_value upload_max_filesize 64M
 +
php_value post_max_size 64M
 +
php_value max_execution_time 300
 +
php_value max_input_time 300
 +
</syntaxhighlight>
 +
::::: No exemplo estamos a limitar en 64MB o tamaño da subida e o tempo de execución en 5 minutos.
  
−
: Por exemplo, cando PHP se executa mediante CGI/FastCGI poden empregarse arquivos '''.user.ini''' para determinadas opcións de configuración.
 
  
 
<br />
 
<br />
−
== Colocar arquivos sensibles ==
 
  
−
* Veremos nas seguintes unidades que teremos que gardar información sensible en certos arquivos PHP, como os datos necesarios para conectarnos a unha base de datos.
+
== Verificando os datos dun formulario ==
  
−
* Podemos definir esa información nun arquivo PHP, por exemplo ''informacionbd.php'', e incorporalo mediante '''require''' ou '''require_once''' nas páxinas que necesiten utilizalo.
+
* Unha forma rápida de verificar que certos datos dun formulario teñan información sería:
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
<?php
 +
// Campos obrigatorios
 +
$campos_obrigatorios = array('txtNome', 'txtEmail');
  
−
* Para evitar que este tipo de arquivos poidan solicitarse directamente desde o navegador, é recomendable situalos fisicamente fóra do '''DocumentRoot''' do servidor web.
+
// Comprobamos que os campos obrigatorios teñan datos
 +
$erro = false;
 +
foreach ($campos_obrigatorios as $campo) {
 +
  if (empty($_POST[$campo])) $erro = true;
 +
}
  
−
: Por exemplo, se Apache publica:
+
if ($erro) {
 +
  // Non existe algún dos campos obrigatorios
 +
}
  
−
::<syntaxhighlight lang="text">
 
−
/var/www/html
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
: podemos gardar o arquivo de configuración en:
+
== 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.
  
−
::<syntaxhighlight lang="text">
+
: Se creo agora o formulario para eses 3 exemplares tería que crear algo así:
−
/var/www/configuracions/informacionbd.php
+
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
<input type='text' name='txtEstado1' />
 +
<input type='text' name='txtEstado2' />
 +
<input type='text' name='txtEstado3' />
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
* Ademais, debemos:
+
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:
 +
::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 +
for($cont=0;$cont<$numExemplares;$cont++){
 +
<input type='text' name='txtEstado[]' />
 +
}
 +
</syntaxhighlight>
 +
 
 +
* 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.
 +
 
 +
 
  
−
:* configurar correctamente os permisos do arquivo;
 
−
:* evitar gardar contrasinais ou credenciais en repositorios públicos;
 
−
:* permitir o acceso unicamente ao usuario ou servizo que realmente o necesite.
 
  
 
<br />
 
<br />
 
== Exercicios propostos ==
 
== Exercicios propostos ==
  
−
* '''Exercicio 1:''' Define nun arquivo externo dentro dun cartafol de nome ''utilidades'' dúas funcións:
+
* '''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.
 +
 
 +
 
 +
 
 +
 
 +
 
 +
 
 +
 
  
−
:* unha función que, a partir dunha cadea, engada dita cadea a un array se non existe previamente;
 
−
:* outra función que, dado un array, devolva unha copia do array recibido.
 
  
−
: Xera unha páxina que faga uso das funcións do arquivo anterior. O arquivo é obrigatorio para o funcionamento da páxina.
 
  
  
−
* '''Exercicio 2:''' Define nun arquivo externo de nome ''utilidades2.php'', dentro do cartafol ''utilidades'', as seguintes constantes:
 
  
−
:* '''NUM_ELEMENTOS''' igual a 3;
 
−
:* '''IDADE_MAIOR''' igual a 18.
 
  
−
: Crea tamén unha función que, dado un array numérico, comprobe que o número de elementos do array sexa superior a '''NUM_ELEMENTOS'''.
 
  
−
: Se non se cumpre esta condición, a función debe devolver '''false'''.
 
  
−
: En caso contrario, debe devolver un array cos elementos que sexan maiores que '''IDADE_MAIOR'''. Se ningún elemento cumpre a condición, debe devolver '''false'''.
 
  
−
: Crea unha páxina de exemplo de uso da función anterior, tendo en conta que a páxina pode xerarse dinamicamente e non sabemos se o arquivo foi incluído previamente. A páxina necesita cargar obrigatoriamente o arquivo ''utilidades2.php''.
 
  
 
<br />
 
<br />
 
=== Solución Exercicios propostos ===
 
=== Solución Exercicios propostos ===
  
−
* '''Solución Exercicio 1'''
+
* '''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.
 +
 
 +
::<syntaxhighlight lang="php" enclose="div" highlight="" >
 +
<!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>
 +
</syntaxhighlight>
 +
 
 +
 
  
−
: '''Páxina utilidades/utilidades1.php'''
+
<br />
 +
* '''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/.
  
−
::<syntaxhighlight lang="php" enclose="div">
+
: '''Arquivo /utilidades/operacionsFroitas.inc.php'''
 +
::<syntaxhighlight lang="php" enclose="div" highlight="" >
 
<?php
 
<?php
 +
// Array para gardar as froitas
 +
$listaFroitas = [];
 +
 +
/**
 +
* Verifica se o nome dunha froita é válido.
 +
*
 +
* Esta función limpa o nome da froita de caracteres non permitidos. 
 +
* - Se o nome está baleiro despois da limpeza, devolve `false`. 
 +
* - Se é válido, devolve o nome da froita limpo.
 +
*
 +
* @param string $nome Nome da froita a verificar.
 +
*
 +
* @return string|false Nome da froita limpo ou `false` se está baleiro.
 +
*/
 +
function verificarNomeFroita(string $nome): string|false {
 +
    // Deprecado: filter_var($nome, FILTER_SANITIZE_STRING);
 +
    // Mellor uso recomendado: filter_var con FILTER_SANITIZE_FULL_SPECIAL_CHARS
 +
    $nome = filter_var($nome, FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES);
 +
 +
    if (empty($nome)) {
 +
        return false;
 +
    } else {
 +
        return $nome;
 +
    }
 +
}
  
 
/**
 
/**
−
  * Engade unha cadea ao array se non existe xa.
+
  * Engade unha froita á lista global se non existe xa.
 
  *
 
  *
−
* @param array  &$array Array onde se engadirá a cadea.
+
  * @param string $nomeFroita Nome da froita a engadir.
−
  * @param string $cadea  Cadea de texto a engadir.
 
 
  *
 
  *
 
  * @return void
 
  * @return void
 
  */
 
  */
−
function engadirCadeaAoArray(array &$array, string $cadea): void
+
function engadirFroita(string $nomeFroita): void {
−
{
+
    global $listaFroitas;
−
     if (!in_array($cadea, $array, true)) {
+
   
−
         $array[] = $cadea;
+
     if (!in_array($nomeFroita, $listaFroitas)) {
 +
         $listaFroitas[] = $nomeFroita;
 
     }
 
     }
 
}
 
}
  
 
/**
 
/**
−
  * Devolve unha copia dun array.
+
  * Elimina unha froita da lista global.
 
  *
 
  *
−
  * En PHP os arrays asígnanse por valor. Polo tanto, devolver o array
+
  * @param string $nomeFroita Nome da froita a eliminar.
−
* permite obter unha copia que pode modificarse sen alterar o array orixinal.
 
 
  *
 
  *
−
  * @param array $array Array que se quere copiar.
+
  * @return void
 +
*/
 +
function eliminarFroita(string $nomeFroita): void {
 +
    global $listaFroitas;
 +
    $indice = array_search($nomeFroita, $listaFroitas);
 +
    if ($indice !== false) {
 +
        unset($listaFroitas[$indice]);
 +
    }
 +
}
 +
 
 +
/**
 +
* Obtén a lista de froitas.
 
  *
 
  *
−
  * @return array Copia do array recibido.
+
  * @return array Array coas froitas actuais na lista global.
 
  */
 
  */
−
function copiarArray(array $array): array
+
function obterListaFroitas(): array {
−
{
+
    global $listaFroitas;
−
     return $array;
+
     return $listaFroitas;
 
}
 
}
 +
 +
 +
?>
 +
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
: '''Páxina exercicio1_include.php'''
 
  
−
::<syntaxhighlight lang="php" enclose="div">
+
: '''Arquivo xestionFroitas.php'''
 +
::<syntaxhighlight lang="php" enclose="div" highlight="" >
 
<!DOCTYPE html>
 
<!DOCTYPE html>
−
<html lang="gl">
+
<html>
 
<head>
 
<head>
−
    <meta charset="UTF-8">
+
     <title>Xestión de Lista de Froitas</title>
−
     <title>Exemplo PHP en Galego</title>
 
 
</head>
 
</head>
 
<body>
 
<body>
−
<?php
+
    <h1>Xestión de Lista de Froitas</h1>
−
require __DIR__ . '/utilidades/utilidades1.php';
+
 
 +
    <?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>";
 +
        }
 +
    }
  
−
$meuArray = [];
+
    // Comprobamos se se enviou unha froita para eliminar
 +
    if (isset($_POST['btnBorrar'])) {
 +
        $froitaAEliminar = $_POST['btnBorrar'];
 +
        eliminarFroita($froitaAEliminar);
 +
    }
  
−
// Engadir cadeas ao array.
+
    // Obtemos a lista de froitas
−
engadirCadeaAoArray($meuArray, 'Ola');
+
    $listaFroitas = obterListaFroitas();
−
engadirCadeaAoArray($meuArray, 'Mundo');
 
−
engadirCadeaAoArray($meuArray, 'Ola'); // Xa existe e non se engadirá de novo.
 
  
−
// Copiar o array.
+
    // Amosamos a táboa de froitas ou a mensaxe de cesta baleira
−
$copiaArray = copiarArray($meuArray);
+
    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>";
 +
    ?>
  
−
echo '<h2>Array orixinal:</h2>';
+
        <label for="novaFroita">Nome da Froita:</label>
−
echo '<pre>';
+
        <input type="text" id="novaFroita" name="novaFroita">
−
print_r($meuArray);
+
        <button type="submit" name='btnEngadir' value='engadir'>Engadir Froita</button>
−
echo '</pre>';
+
        <?php if(!empty($listaFroitas)) ?>
 +
            <div><button type="submit" name='btnBaleirar' value='baleirar'>Baleirar Cesta</button></div>
 +
           
  
−
echo '<h2>Copia do array:</h2>';
+
    </form>
−
echo '<pre>';
 
−
print_r($copiaArray);
 
−
echo '</pre>';
 
−
?>
 
 
</body>
 
</body>
 
</html>
 
</html>
 +
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
  
 
<br />
 
<br />
−
* '''Solución Exercicio 2'''
+
* '''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 utilidades/utilidades2.php'''
 
  
−
::<syntaxhighlight lang="php" enclose="div">
+
: '''Páxina usuarios.inc.php'''
 +
::<syntaxhighlight lang="php" enclose="div" highlight="" >
 
<?php
 
<?php
−
 
+
// Array de datos de usuarios (id, nome, tfno, email)
−
const NUM_ELEMENTOS = 3;
+
$usuarios = [
−
const IDADE_MAIOR = 18;
+
    ["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"]
 +
];
  
 
/**
 
/**
−
  * Comproba as idades dun array e devolve os elementos que superen un límite.
+
  * Obtén os datos dun usuario a partir do seu ID.
 +
*
 +
* Esta función percorre a lista global de usuarios e devolve o usuario cuxo ID coincida co proporcionado. 
 +
* - Se non se atopa ningún usuario con ese ID, devolve `null`.
 
  *
 
  *
−
  * @param array $array Array de números (idades) a comprobar.
+
  * @param int $id ID do usuario que se quere buscar.
 
  *
 
  *
−
  * @return array|false Devolve un array cos elementos maiores que IDADE_MAIOR
+
  * @return array|null Array asociativo coas datos do usuario se se atopa, ou `null` se non existe.
−
*                    ou false se non se cumpren as condicións.
 
 
  */
 
  */
−
function comprobarIdade(array $array): array|false
+
function obterUsuarioPorID(int $id): array|null {
−
{
+
    global $usuarios;
−
    $elementosCumpridos = [];
+
   
 +
    foreach ($usuarios as $usuario) {
 +
        if ($usuario["id"] == $id) {
 +
            return $usuario;
 +
        }
 +
    }
 +
   
 +
    return null; // Devolve null se o usuario non se atopa
 +
}
 +
?>
 +
</syntaxhighlight>
 +
 
 +
: '''Páxina listaUsuarios.php'''
 +
::<syntaxhighlight lang="php" enclose="div" highlight="" >
 +
<!DOCTYPE html>
 +
<html>
 +
<head>
 +
    <title>Listado de Usuarios</title>
 +
</head>
 +
<body>
 +
    <h1>Listado de Usuarios</h1>
  
−
     if (count($array) <= NUM_ELEMENTOS) {
+
     <?php
−
        return false;
+
    require_once 'utilidades/usuarios.inc.php';
−
    }
 
  
−
     foreach ($array as $idade) {
+
    // Amosar a lista de usuarios como enlaces
−
         if ($idade > IDADE_MAIOR) {
+
    echo "<ul>";
−
            $elementosCumpridos[] = $idade;
+
     foreach ($usuarios as $usuario) {
−
        }
+
         echo "<li><a href='detalleUsuario.php?id={$usuario["id"]}'>{$usuario["nome"]}</a></li>";
 
     }
 
     }
 +
    echo "</ul>";
 +
    ?>
 +
</body>
 +
</html>
  
−
    return $elementosCumpridos !== [] ? $elementosCumpridos : false;
 
−
}
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
: '''Páxina exercicio2_include.php'''
 
  
−
::<syntaxhighlight lang="php" enclose="div">
+
; '''Páxina detalleUsuario.php'''
 +
::<syntaxhighlight lang="php" enclose="div" highlight="" >
 
<!DOCTYPE html>
 
<!DOCTYPE html>
−
<html lang="gl">
+
<html>
 
<head>
 
<head>
−
    <meta charset="UTF-8">
+
     <title>Detalles do Usuario</title>
−
     <title>Exemplo PHP en Galego</title>
 
 
</head>
 
</head>
 
<body>
 
<body>
−
<?php
+
    <h1>Detalles do Usuario</h1>
−
require_once __DIR__ . '/utilidades/utilidades2.php';
 
  
−
$idades = [2, 62, 2, 2, 2]; // Soamente 62 cumpre a condición.
+
    <?php
 +
    require 'utilidades/usuarios.inc.php';
  
−
$resultado = comprobarIdade($idades);
+
    // 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);
  
−
if ($resultado !== false) {
+
        // Amosar os detalles do usuario
−
    echo '<h2>Idades que cumpren a condición:</h2>';
+
        if ($usuarioSeleccionado !== null) {
−
    echo '<ul>';
+
            echo "<p>Nome: {$usuarioSeleccionado["nome"]}</p>";
−
 
+
            echo "<p>Teléfono: {$usuarioSeleccionado["tfno"]}</p>";
−
     foreach ($resultado as $idade) {
+
            echo "<p>Email: {$usuarioSeleccionado["email"]}</p>";
−
         echo "<li>$idade anos</li>";
+
            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>";
 
     }
 
     }
−
 
+
     ?>
−
     echo '</ul>';
 
−
} else {
 
−
    echo '<h2>Ningunha idade cumpre a condición</h2>';
 
−
}
 
−
?>
 
 
</body>
 
</body>
 
</html>
 
</html>
 +
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
<br> -- [[Usuario:angelfg|Ángel D. Fernández González]] -- (2026).
+
 
 +
<br> -- [[Usuario:angelfg|Ángel D. Fernández González]] -- (2025).

Revisión del 17:03 13 sep 2026

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_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES);
Devolve 'Isto si aparece'.


  • 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_FULL_SPECIAL_CHARS 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_FULL_SPECIAL_CHARS 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)
  • htmlspecialchars: Moi parecida á anterior, ten a mesma función pero é máis lixeira a nivel de carga de CPU xa que elimina soamente aqueles caracteres que poidan ser empregados para un ataques XSS). Normalmente a empregaremos con estes argumentos: $novoDato = htmlspecialchars($dato, ENT_QUOTES, 'UTF-8');
É a opción a empregar cando queramos visualizar datos que proveñan da base de datos ou dun campo de formulario.


No caso do formulario poderíamos poñer:

<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES, 'UTF-8') ?>" 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 mysqli_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.
  • Si existe código de script posteriormente á chamada do header será necesario poñer exit; despois da orde header para que non sigua a executar o script.


Tamén indicar que como veremos posteriormente cando enviamos datos con get, estes datos aparecen na páxina en forma de paráemtros &param1=valor1&param2=valor2&...polo tanto podemos facer unha redirección enviando datos na URL da forma:
1 <?php
2 header('Location: http://www.nova_paxina.es?parametro1=valor1&paramentro2=valor2');
3 ?>
E na seguinte páxina obter os datos con $_GET['parametro1'],...





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 = [];

/**
 * Verifica se o nome dunha froita é válido.
 *
 * Esta función limpa o nome da froita de caracteres non permitidos.  
 * - Se o nome está baleiro despois da limpeza, devolve `false`.  
 * - Se é válido, devolve o nome da froita limpo.
 *
 * @param string $nome Nome da froita a verificar.
 *
 * @return string|false Nome da froita limpo ou `false` se está baleiro.
 */
function verificarNomeFroita(string $nome): string|false {
    // Deprecado: filter_var($nome, FILTER_SANITIZE_STRING);
    // Mellor uso recomendado: filter_var con FILTER_SANITIZE_FULL_SPECIAL_CHARS
    $nome = filter_var($nome, FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES);

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

/**
 * Engade unha froita á lista global se non existe xa.
 *
 * @param string $nomeFroita Nome da froita a engadir.
 *
 * @return void
 */
function engadirFroita(string $nomeFroita): void {
    global $listaFroitas;
    
    if (!in_array($nomeFroita, $listaFroitas)) {
        $listaFroitas[] = $nomeFroita;
    }
}

/**
 * Elimina unha froita da lista global.
 *
 * @param string $nomeFroita Nome da froita a eliminar.
 *
 * @return void
 */
function eliminarFroita(string $nomeFroita): void {
    global $listaFroitas;
    $indice = array_search($nomeFroita, $listaFroitas);
    if ($indice !== false) {
        unset($listaFroitas[$indice]);
    }
}

/**
 * Obtén a lista de froitas.
 *
 * @return array Array coas froitas actuais na lista global.
 */
function obterListaFroitas(): array {
    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"]
];

/**
 * Obtén os datos dun usuario a partir do seu ID.
 *
 * Esta función percorre a lista global de usuarios e devolve o usuario cuxo ID coincida co proporcionado.  
 * - Se non se atopa ningún usuario con ese ID, devolve `null`.
 *
 * @param int $id ID do usuario que se quere buscar.
 *
 * @return array|null Array asociativo coas datos do usuario se se atopa, ou `null` se non existe.
 */
function obterUsuarioPorID(int $id): array|null {
    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 -- (2025).