Diferencia entre revisiones de «PHP Operacións BD Mysql»
| Línea 246: | Línea 246: | ||
== Visualización de errores == | == Visualización de errores == | ||
| − | * | + | * Ao principio ides cometer moitos erros durante as probas de programación. |
| − | : PHP | + | : PHP non vai a amosar nada e simplemente vai amosar unha páxina en branco. |
| − | * Para saber | + | * Para saber onde está o erro, pódense cambiar as directivas '''display_errors''' e '''error_reporting''' do php.ini, pero se non queredes cambiar ditos arquivos, podedes poñer ao principio de cada páxina PHP o seguinte: |
:<syntaxhighlight lang="java" enclose="div" highlight="" > | :<syntaxhighlight lang="java" enclose="div" highlight="" > | ||
ini_set('display_errors', 1); | ini_set('display_errors', 1); | ||
| Línea 258: | Línea 258: | ||
| − | : <u> | + | : '''IMPORTANTE:'''<u>Lembrar quitar ditas liñas cando rematedes de programar a páxina.</u> |
| + | |||
| + | |||
<br /> | <br /> | ||
| + | |||
== Conexión == | == Conexión == | ||
Revisión del 15:00 23 oct 2023
Sumario
- 1 Introdución
- 2 Activar a extensión mysqli
- 3 Creando as táboas en Mysql
- 4 Visualización de errores
- 5 Conexión
- 6 Cambiando de base de datos activa
- 7 Operacións contra a base de datos
- 8 Transaccións
- 9 Permisos mínimos
- 10 Optimización: Sentenzas Prepared
- 11 Gardando campos a protexer na base de datos
- 12 Subindo unha imaxe ao servidor
Introdución
- Aclaración importante: A partires da versión 8.1 de PHP, as ordes que imos ver a continuación lanzan excepcións. Os exercicios resoltos están feitos con dita versión. Cando cheguedes a sección de Visualización de Errores se indicarán as dúas formas de xestionar os erros.
- Como comentamos no punto anterior, para conectarnos a un xestor de base de datos específico imos ter que ter instalados os drivers correspondentes.
- Un driver é o software que nos permite comunicarnos cun xestor de base de datos, pero normalmente non imos facer dita comunicación utilizando o driver directamente se non que o usaremos a través dunha API, que son un conxunto de clases, funcións,... que nos permiten facer uso do driver e conectar a un xestor de base de datos.
- Existen dous tipos de API´s:
- Procedimentais: Facemos uso de funcións para realizar as operacións contra a base de datos.
- Orientadas a obxectos: Instanciamos clases e chamamos a métodos dos obxectos creados.
Normalmente faremos uso desta segunda opción.
- En PHP, ditas API´s se 'activan' facendo uso de Extensións.
- Indicar que unha API non é o mesmo que unha extensión en PHP, xa que algunhas extensións non proporcionan ningunha API ao usuario (por exemplo a extensión do driver PDO MySQL, non proporciona ningunha API ó programador PHP).
- Existen tres APIs principais para conectar a un servidor de bases de datos MySQL:
- Extensión MySQL de PHP
- Extensión mysqli de PHP
- Obxectos de Datos de PHP (PDO)
- Para conectarnos imos utilizar as funcións mysqli (mysql improved) que foron introducidas a partires de Mysql 4.1
- A extensión mysqli ven incluída nas versiones PHP 5 e posteriores.
- Algunha das melloras de dita extensión con respecto á extensión mysql:
- Interfaz orientada a obxectos
- Soporte para declaracións preparadas.
- Soporte para Transaccións
- Dita extensión tamén ten unha versión procedural, de tal forma que imos poder chamar ás funcións de dúas maneiras diferentes:
Exemplo de función cun estilo procedimental que pecha unha conexión a unha base de datos ($link é un identificador cando se fai a conexión contra a base de datos):
1 mysqli_close($link);
Exemplo de función cun estilo obxectual que pecha unha conexión a unha base de datos:
1 mysqli::close();
- Aquí tedes un cadro comparativo obtido de php.net onde se ve o comentado anteriormente:
Nota: Máis información neste enlace.
Activar a extensión mysqli
- Se instalamos XAMPP ou calquera das versións para outros S.O. normalmente a extensión xa ven habilitada por defecto.
- Se temos unha instalación personalizada no que instalamos separadamente todo, teremos que habilitala.
Windows
- En Windows, a extensión mysqli ven desactivada no php.ini.
- Partindo de que xa tedes o arquivo php.ini no cartafol onde se atopa o intérprete de PHP (se non é así mirade a nota de Importante que se atopa neste punto da wiki) temos que:
- Descomentar a liña:
1 ;extension=php_mysqli.dll
- Pasa a:
1 extension=php_mysqli.dll
- Descomentar a liña:
1 ;extension_dir = "ext"
- Pasa a:
1 extension_dir = "ext"
Linux
- Necesitamos ter instalado o paquete: php-mysql.
- Para instalalo:
1 sudo apt install php-mysql
- Reiniciamos o apache: sudo systemctl restart apache2
- O anterior vai crear varios arquivos de configuración no cartafol: etc/php/8.1/apache2/conf.d para cargar as extensións mysqli e pdo.
- Nota: O cartafol pode variar dependendo da versión php que teñades instalada.
Creando as táboas en Mysql
- Unha vez temos a base de datos teremos que facer o Modelo EE/R, pasalo ao Modelo Relacional e facer un Proceso de Normalización ata polo menos a forma normal de Boyce-Codd.
- Imos implementar unha base de datos para xestionar a venda de libros de segunda mano.
- A orde SQL para crear unha base de datos de nome php é:
CREATE SCHEMA `php` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_spanish2_ci ;
- O modelo EE/R é o seguinte:
Nota:
- A táboa ESTADOS podería eliminarse e crear un campo estado de tipo enum en Mysql se ditos 'estados' van ser sempre os mesmos e non van modificarse...
- O campo 'imaxe' da táboa EXEMPLARES está nesta táboa e non na táboa LIBROS, xa que a idea é amosar ao comprador unha imaxe do exemplar para amosar o seu estado.
- O modelo relacional implementado nunha base de datos Mysql é o seguinte:
CREATE TABLE IF NOT EXISTS `php`.`libros` ( `id` INT NOT NULL AUTO_INCREMENT, `titulo` VARCHAR(300) NOT NULL, PRIMARY KEY (`id`)); CREATE TABLE IF NOT EXISTS `php`.`estados` ( `id` INT NOT NULL AUTO_INCREMENT, `descripcion` VARCHAR(100) NOT NULL, PRIMARY KEY (`id`)); CREATE TABLE IF NOT EXISTS `php`.`exemplares` ( `libro_id` INT NOT NULL, `id_exemplar` INT NOT NULL, `imaxe` VARCHAR(100) NULL, `prezo` DECIMAL(6,2) NOT NULL, `estado_id` INT NULL, INDEX `fk_EXEMPLARES_LIBROS_idx` (`libro_id` ASC), INDEX `fk_EXEMPLARES_ESTADOS_idx` (`estado_id` ASC), PRIMARY KEY ( `libro_id`,`id_exemplar`), CONSTRAINT `fk_EXEMPLARES_LIBROS` FOREIGN KEY (`libro_id`) REFERENCES `php`.`libros` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, CONSTRAINT `fk_EXEMPLARES_ESTADOS` FOREIGN KEY (`estado_id`) REFERENCES `php`.`estados` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE); CREATE TABLE IF NOT EXISTS `php`.`autores` ( `id` INT NOT NULL AUTO_INCREMENT, `apelidos` VARCHAR(200) NOT NULL, `nome` VARCHAR(50) NOT NULL, PRIMARY KEY (`id`)); CREATE TABLE IF NOT EXISTS `php`.`libro_autor` ( `libro_id` INT NOT NULL, `autor_id` INT NOT NULL, PRIMARY KEY (`libro_id`, `autor_id`), INDEX `fk_LIBROAUTOR_AUTORES_idx` (`autor_id` ASC), CONSTRAINT `fk_LIBROAUTOR_LIBROS` FOREIGN KEY (`libro_id`) REFERENCES `php`.`libros` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, CONSTRAINT `fk_LIBROAUTOR_AUTORES` FOREIGN KEY (`autor_id`) REFERENCES `php`.`autores` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE);
- Para ter algún dato inicial imos a executar estas ordes:
INSERT INTO `php`.`estados` VALUES (1,'NOVO'),(2,'COMO NOVO'),(3,'DETERIODADO'),(4,'CON ANOTACIÓNS'); INSERT INTO `php`.`autores` (`apelidos`, `nome`) VALUES ('Drumont', 'Eduardo'); INSERT INTO `php`.`libros` (`titulo`) VALUES ('El caso Dreyfus, expresión del antisemitismo, temas, Gallica, BNF'); INSERT INTO `php`.`libros` (`titulo`) VALUES ('Les amis'); INSERT INTO `php`.`exemplares` (`libro_id`, `id_exemplar`, `imaxe`, `prezo`, `estado_id`) VALUES ('1', '1', 'dreyfus.jpg', '20.52', '2'); INSERT INTO `php`.`exemplares` (`libro_id`, `id_exemplar`, `imaxe`, `prezo`, `estado_id`) VALUES ('1', '2', 'dreyfus.jpg', '15.34', '3'); INSERT INTO `php`.`exemplares` (`libro_id`, `id_exemplar`, `imaxe`, `prezo`, `estado_id`) VALUES ('2', '1', 'amis.jpg', '35.35', '1'); INSERT INTO `php`.`exemplares` (`libro_id`, `id_exemplar`, `imaxe`, `prezo`, `estado_id`) VALUES ('2', '2', 'amis.jpg', '20.25', '4'); INSERT INTO `php`.`exemplares` (`libro_id`, `id_exemplar`, `imaxe`, `prezo`, `estado_id`) VALUES ('2', '3', 'amis.jpg', '25.34', '3'); INSERT INTO `php`.`libro_autor` VALUES (1,1),(2,1);
- Agora faremos o seguinte:
- Crearemos dentro do noso sitio web (normalmente /var/www/html/) un cartafol de nome IMAXES
- Copiamos as imaxes que veñen a continuación a dito cartafol.
- Cambiaremos o propietario coa orde chown -R www-data:www-data IMAXES/
Nome: amis.jpg
Nome: dreyfus.jpg
Regras de nomeado de táboas e columnas
- Aínda que non existen 'regras' únicas sobre como nomear as táboas e campos nos imos seguir as seguintes, para que cando vexamos Laravel, coincidan os Modelos de Laravel cós nomes das táboas asociadas:
- Nomes de táboas en PLURAL e con minúsculas. Por exemplo táboa: usuarios
- Nomes de campos en minúscula e singular.
- Se o campo é a clave primaria terá de nome: id
- Se o campo é unha clave foránea terá de nome: nometáboa_en_singular_id. Por exemplo, nunha táboa de nome pais, asociada á táboa usuarios cunha relación 1:N, tería unha clave foránea de nome usuario_id.
- Se o nome de campo está formado por varias palabras, estarán separadas por un guión baixo ( _ )
- Non usar til nin caracteres 'raros' como espazos en branco, interrogacións,...
- As claves foráneas irán ao final da lista de columnas da táboa.
- As táboas que se formen por unha relación N:M terán de nome: taboa1singular_taboa2singular. Por exemplo se temos unha táboa usuarios e unha táboa modulos e existe unha relación N:M entre elas, se crearía a táboa: usuario_modulo
Visualización de errores
- Ao principio ides cometer moitos erros durante as probas de programación.
- PHP non vai a amosar nada e simplemente vai amosar unha páxina en branco.
- Para saber onde está o erro, pódense cambiar as directivas display_errors e error_reporting do php.ini, pero se non queredes cambiar ditos arquivos, podedes poñer ao principio de cada páxina PHP o seguinte:
ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL);
- IMPORTANTE:Lembrar quitar ditas liñas cando rematedes de programar a páxina.
Conexión
- Partimos da base de que xa temos instalado:
- O xestor de bases de datos Mysql
- Unha base de datos creada. Neste manual vai ter de nome php. Facer que o 'default collation' sexa utf8mb4_spanish2_ci (máis información sobre o charset/collation neste enlace.
- Un usuario / password que teña todos os permisos sobre a base de datos creada. Neste manual o usuario/password creado son user_php/user_php.
- Todo o anterior xa está indicado no punto anterior da wiki.
Apertura da conexión
- Utilizando procedementos: mysqli_connect
- Utilizando obxectos: mysql::_construct
- Sintaxe (se indica a sintaxe do construtor e polo tanto será equivalente a facer un 'new' da clase mysqli):
- Orientada a obxectos:
mysqli::__construct ([ string $host = ini_get("mysqli.default_host") [, string $username = ini_get("mysqli.default_user") [, string $passwd = ini_get("mysqli.default_pw") [, string $dbname = "" [, int $port = ini_get("mysqli.default_port") [, string $socket = ini_get("mysqli.default_socket") ]]]]]] )
- Por procedementos:
mysqli mysqli_connect ([ string $host = ini_get("mysqli.default_host") [, string $username = ini_get("mysqli.default_user") [, string $passwd = ini_get("mysqli.default_pw") [, string $dbname = "" [, int $port = ini_get("mysqli.default_port") [, string $socket = ini_get("mysqli.default_socket") ]]]]]] )
- Todos eles son optativos, pero normalmente indicaremos o nome de host, login, password e base de datos.
- Os parámetros son:
- Host a conectar (cadea): Pode ser o nome ou a dirección IP. Normalmente imos poñer 'localhost' (se o xestor de bases de datos está instalado no mesmo equipo que o servidor web).
- Nome do usuario con acceso á base de datos (cadea): Nome do usuario que ten acceso á base de datos.
- Password (cadea): Password do usuario.
- Dbname (cadea): Nome da base de datos sobre a que imos traballar.
- Porto (numérico): Porto no que está escoitando o xestor de base de datos. No caso de mysql é o 3306.
- Socket (cadea): Socket ou tubería para conectar có xestor de bases de datos. En Linux é un tipo especial de arquivo que serve para 'transmitir' información, neste caso, cara o xestor de bases de datos.
- Aplicado ao noso caso, teremos a seguinte orde:
- Por procedementos:
1 $conex = mysqli_connect('localhost','user_php','user_php','php');
- Orientado a obxectos:
1 $conex = new mysqli('localhost','user_php','user_php','php');
- En caso de erro, $conex é un obxecto que represente a conexión á base de datos.
Control de errores
- En versións de PHP igual ou superior á 8.1: Neste caso, debemos empregar a forma try ... catch da programación orientada a obxectos:
- Versión con chamada por procedemento teremos que chamar á función mysqli_connect_error() desta forma:
1 try { 2 $conex = mysqli_connect('localhost', 'usuario_inexistente', 'contrasena_incorrecta', 'base_de_datos'); 3 } catch (mysqli_sql_exception $e) { 4 die("Erro de conexión: " . $e->getMessage()); 5 }
- Versión con chamada empregando obxectos:
1 try { 2 $conex = new mysqli('localhost', 'usuario_inexistente', 'contrasena_incorrecta', 'base_de_datos'); 3 } catch (mysqli_sql_exception $e) { 4 die("Erro de conexión: " . $e->getMessage()); 5 }
- Nota:
- Normalmente ao usuario non se lle vai amosar o erro que devolve o xestor de base de datos e dito erro sóese gardar nunha táboa interna para o administrador e que poida ver cal foi o erro.
- Podemos facer un único try-catch que envolva toda a páxina PHP
- Ou facer un try-catch por cada bloque php que poida xerar unha excepción.
- Por exemplo, imos modificar o password do usuario por un que non exista e veremos o resultado:
1 try { 2 $conex = mysqli_connect('localhost','user_php','password_incorrecta','php'); 3 } catch (mysqli_sql_exception $e) { 4 die("Erro de conexión: (" . $e->getCode() . ') ' . $e->getMessage()); 5 }
- Dará como resultado:
- En versións de PHP inferior á 8.1
- En caso de ter unha versión PHP anterior á PHP 5.2.9 e 5.3.0 ou estamos a utilizar a versión de chamadas por procedementos, teremos que chamar á función mysqli_connect_error() desta forma:
1 $conex = mysqli_connect('localhost','user_php','user_php','php'); 2 if (mysqli_connect_error()){ 3 die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); 4 }
- En caso de ter unha versión PHP posterior á PHP 5.2.9 e 5.3.0 e estar utilizando a forma orientada a obxectos:
1 $conex = new mysqli('localhost','user_php','user_php','php'); 2 if ($conex->connect_error) { 3 die('Erro de Conexión (' . $conex->connect_errno . ') ' . $conex->connect_error); 4 }
- Como vemos mostramos por un lado a mensaxe de erro que devolve o xestor de bases de datos e o número de erro asociado a esa mensaxe.
- Podemos consultar os número de erro e mensaxes asociadas neste enlace.
- Nota: Normalmente ao usuario non se lle vai amosar o erro que devolve o xestor de base de datos e dito erro sóese gardar nunha táboa interna para o administrador e que poida ver cal foi o erro.
Gardando os datos de conexión nun lugar seguro
- Como é obvio os datos de base de datos, usuario e password son moi importantes e non debemos permitir que alguén poida descubrilos.
- O que faremos será o seguinte:
- Definiremos en forma de constantes ditos datos nun arquivo de include.
- Gardaremos dito arquivo fora do cartafol de acceso do noso sitio web.
- O primeiro punto, definir en forma de constante e nun arquivo de include os datos da conexión:
Arquivo: bd.inc.php:
1 <?php 2 3 define ('SERVIDOR','localhost'); 4 define ('USUARIO','user_php'); 5 define ('PASSWORD','user_php'); 6 define ('BD','php');
- Faremos un require deste arquivo en todas as páxinas que necesiten unha conexión á base de datos. Desta forma, se necesitamos cambiar algo soamente o cambiaríamos nun arquivo.
- O segundo punto consiste en descubrir cal é a ruta física á que apunta o noso servidor web e colocar o arquivo de include 'fora' desa ruta.
- Temos neste punto da Wiki información sobre como funciona Apache e como saber a onde apunta o servidor web no seu arquivo de configuración.
- Temos varias opcións:
- Facendo uso da constante __DIR__.
- Facendo uso do array global $_SERVER e dentro deste a entrada $_SERVER['DOCUMENT_ROOT'].
- Se facemos un echo destas variables dentro dunha páxina do noso sitio web, obteremos unha ruta, coma por exemplo:
1 /var/www/html/
- Nota: Se o facemos coa constante __DIR__ e a páxina que está a facer o echo se atopa dentro dun cartafol, tamén aparecerá o cartafol (por exemplo /var/www/html/cartafol/).
- Isto quere dicir que o servidor web (e polo tanto calquera usuario de Internet) vai poder acceder as páxinas que se atopan no cartafol /var/www/html e calquera dentro del.
- Unha medida de seguridade é colocar o arquivo de include 'fora' desta ruta. Por exemplo, podemos crear un arquivo no cartafol /var/www/confBD
- A dito cartafol ten que poder acceder o usuario que utiliza o Apache para acceder aos cartafoles a nivel do S.O. (no caso de Apache soe ser www-data, podedes miralo no punto da wiki indicado anteriormente).
- Creando o cartafol onde se vai gardar o arquivo de configuración
Cambiamos os permisos: sudo chmod -R 550 /var/www/confBD e cambiamos o propietario: sudo chown -R www-data:www-data /var/www/confBD. Nota: O usuario e grupo pode variar dependendo do S.O. En Linux podedes sabelo mirando este enlace da wiki. En Windows o podedes saber neste enlace.
- Agora, calquera páxina que faga uso destes datos terá que facer o seguinte require:
1 <?php 2 3 //error_reporting(0); // Inabilita os erros de Apache se temos activada a directiva display_erros no php.ini 4 5 6 require ('/var/www/confBD/bd.inc.php'); 7 $conex = new mysqli(SERVIDOR,USUARIO,PASSWORD,BD);
- Se queremos facer o mesmo pero sen utilizar rutas absolutas (por exemplo se estamos nun aloxamento compartido):
1 <?php 2 3 //error_reporting(0); // Inabilita os erros de Apache se temos activada a directiva display_erros no php.ini 4 5 6 $ruta = substr($_SERVER['DOCUMENT_ROOT'],0,strpos($_SERVER['DOCUMENT_ROOT'],'html')) . 'confBD'; // RUTA al sitio web sin /html pero con /confBD 7 require ($ruta.'/bd.inc.php'); 8 9 $conex = new mysqli(SERVIDOR,USUARIO,PASSWORD,BD);
O xogo de caracteres
- Debemos ter en conta que unha páxina web está 'aberta' a todo o mundo que se conecte a ela.
- Cada persoa en diferentes países terá diferente xogos de caracteres no que cada carácter está relacionado cun código numérico.
- Se gardamos como dato dentro da base de datos unha cadea cun 'ñ' entre os seus datos, dito carácter estará asociado a un código nun xogo de caracteres. Se o usuario que vai cargar a páxina o carga cun xogo de caracteres diferente non vai poder visualizalo.
- Cando creamos aplicacións en PHP con acceso a base de datos teremos que ter en conta os seguintes xogos de caracteres:
- Xogo de caracteres da base de datos (no noso caso Mysql): É o que se coñece como Charset/Collation.
- Se vos fixades cando creamos a base de datos en Mysql, escollemos o charset (primeira columna) e o collation (segunda columna).
- Como indicamos antes, cando creamos a base de datos o charset/collation indicado foi utf8mb4/utf8mb4_spanish2_ci.
- O charset é como mysql garda internamente os caracteres e os relaciona cun código numérico e o collation indica como comparar e ordenar o texto. Podedes consultar esta blog para obter máis información.
- Xogo de caracteres da páxina HTML.
- Na páxina web indicamos coa etiqueta <meta> o xogo de caracteres para que o navegador web poida visualizalos correctamente.
- No caso que nos ocupa teremos que poñer:
- Versións inferiores a Html 5: <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
- Html 5: <meta charset="UTF-8">
- No caso que nos ocupa teremos que poñer:
- Máis información en w3schools.
- No código PHP ao conectar coa base de datos.
- Para iso teremos que facer uso da función set_charset.
- En caso de éxito devolve true ou false en caso de existir algún problema.
- Cando conectemos coa base de datos teremos que indicarlle que imos utilizar o xogo de caracteres utf8mb4 con esta liña:
- Versión procedimental
1 try { 2 $conex = mysqli_connect('localhost', 'user_php', 'user_php', 'php'); 3 mysqli_set_charset($conex,'utf8mb4'); 4 } catch (mysqli_sql_exception $e) { 5 die("Erro de conexión: " . $e->getMessage()); 6 }
- Cando conectemos coa base de datos teremos que indicarlle que imos utilizar o xogo de caracteres utf8mb4 con esta liña:
- Versión orientada a obxectos
1 try { 2 $conex = new mysqli('localhost', 'user_php', 'user_php', 'php'); 3 $conex->set_charset('utf8mb4'); 4 } catch (mysqli_sql_exception $e) { 5 die("Erro de conexión: " . $e->getMessage()); 6 }
- E FUNDAMENTAL QUE NOS TRES SITIOS ESTEA POSTO O MESMO TIPO DO XOGO DE CARACTERES.
Peche da conexión
- Despois de facer as diferentes operacións sobre a base de datos, teremos que pechar a conexión.
- Para iso temos que chamar á función mysqil::close.
Nota: En todas as páxinas se opta por facer un único try-catch, pero dependendo de se se quere saber a sección onde foi o erro, se podería facer varios try-catch.
- Versión procedimental
<?php try { // O try se pecha na sección catch do final $conex = mysqli_connect('localhost', 'user_php', 'user_php', 'php'); mysqli_set_charset($conex,'utf8mb4'); ?> .............. // Operacións contra a base de datos e amosar información ao usuario .............. <?php } // Fin do try do principio da páxina catch (mysqli_sql_exception $e) { die("Erro mysql: " . $e->getMessage()); } catch (Expcetion $e) { die("Erro xeral: " . $e->getMessage()); } finally{ if(!empty($conex)) { mysqli_close($conex); } } ?>
- Versión orientada a obxectos
<?php try { $conex = new mysqli('localhost', 'user_php', 'user_php', 'php'); $conex->set_charset('utf8mb4'); ?> .............. // Operacións contra a base de datos e amosar información ao usuario .............. <?php } // Fin do try do principio da páxina catch (mysqli_sql_exception $e) { die("Erro mysql: " . $e->getMessage()); } catch (Expcetion $e) { die("Erro xeral: " . $e->getMessage()); } finally{ if(!empty($conex)){ $conex->close(); } } ?>
Cambiando de base de datos activa
- Cando creamos a cadea de conexión, un dos parámetros que enviamos foi a base de datos na que iamos traballar.
- Podemos non enviar ese parámetro ou querer cambiar de base de datos dentro dunha páxina php.
- Se queremos escoller outra diferente teremos que facer uso da función mysqli::select_db.
- Por exemplo:
- Versión procedimental:
<?php try { $conex = mysqli_connect('localhost', 'user_php', 'user_php'); // Non indicamos o nome da bd mysqli_set_charset($conex,'utf8mb4'); mysqli_select_db($conex, "php"); // O segundo parámetro indica o nome da base de datos ?> .............. // Operacións contra a base de datos e amosar información ao usuario .............. <?php } // Fin do try do principio da páxina catch (mysqli_sql_exception $e) { die("Erro mysql: " . $e->getMessage()); } catch (Expcetion $e) { die("Erro xeral: " . $e->getMessage()); } finally{ if(!empty($conex)){ mysqli_close($conex); } } ?>
- Versión orientada a obxectos:
<?php try { $conex = new mysqli('localhost', 'user_php', 'user_php'); // Non indicamos o nome da bd $conex->set_charset('utf8mb4'); $conex->select_db('php'); // O parámetro indica o nome da base de datos ?> .............. // Operacións contra a base de datos e amosar información ao usuario .............. <?php } // Fin do try do principio da páxina catch (mysqli_sql_exception $e) { die("Erro mysql: " . $e->getMessage()); } catch (Expcetion $e) { die("Erro xeral: " . $e->getMessage()); } finally{ if(!empty($conex)){ $conex->close(); } } ?>
Operacións contra a base de datos
- Operacións de consulta
- Operacións de alta
- Operacións de baixa
- Operacións de modificación
- Exercicios propostos
Transaccións
- En certas ocasións imos necesitar facer varias operacións que conleven modificacións nas táboas da base de datos (operacións de INSERT, UPDATE, DELETE). Se queremos que nunha páxina php se fagan varias modificacións e queremos que se hai calquera problema nunha delas, deixe o estado da base de datos xusto a como estaba antes de facer a primeira operación, necesitamos facer uso das transaccións.
- Por exemplo, no exemplo que estamos a desenvolver:
- Podemos crear unha páxina PHP cun formulario para dar de alta un libro xunto cos exemplares.
- Podemos crear unha páxina PHP que relacione libros xunto cos autores que o escribiron.
- Podemos dar de baixa un libro. Isto leva consigo dar de baixa o libro máis os exemplares máis os datos da táboas LIBROS_AUTORES.
- Outro caso típico é o carrito da compra. Cando o usuario remata o proceso de compra e procede a realizar o pedido, necesitaremos gardar os produtos que conforman o pedido na nosa base de datos.
- Queremos que se garden todos os produtos.
- Normalmente imos queres que ditas operacións se fagan todas e en caso de erro, necesitaremos volver ao estado anterior ao comezo do inicio das operacións.
- Isto o podemos facer grazas as TRANSACCIÓNS.
- Unha transacción nos garante que o conxunto de operacións que están dentro dunha transacción se farán todas ou non se fará ningunha.
- Normalmente cada operación individual é 'considerada' coma unha transacción en si mesma, de tal forma o ou ten éxito ou os cambios non se gardan na base de datos.
- Máis información sobre Transaccións:
- Cando falamos de transaccións outro factor a ter en conta é o chamado Nivel de illamento.
- Imaxinemos que estamos dentro dunha transacción e estou lendo o número de filas dunha táboa. Se outro usuario engade unha nova fila despois de que eu lera o número, e eu dentro da transacción volvo a ler o número de filas. Terei dúas cantidades diferentes dentro da mesma transacción.
- Para evitalo podemos facer que a táboa que estou a utilizar dentro da transacción quede 'bloqueada' e non se poida modificar ata que eu remate.
- Este e outros problemas que poden darse no uso de transaccións se intentan evitar cun nivel de illamento determinado.
- Cada nivel de illamento soluciona algún dos problemas que se poden dar.
- Máis información sobre Niveis de illamento en Mysql:
- Dos niveis de illamento (que se basean en bloquear rexistros de táboas) se pode provocar outro problema, que é o Interbloqueo ou Deadlock.
- Isto prodúcese cando unha transacción_1 está esperando o acceso a un recurso que está sendo bloqueado por outra transacción_2 e esta outra transacción_2 está esperando por un recurso que está sendo bloqueado pola transacción_1.
- Máis información sobre os DeadLock neste enlace.
- En PHP, para poder utilizar transaccións necesitamos:
- MySQL 5.6
- O motor InnoDB.
- Unha transacción a podemos facer dende a páxina PHP ou dende o xestor Mysql dentro dun procedemento almacenado.
- Para realizar unha transacción o proceso sempre é o mesmo en todas as linguaxes e xestores.
- Iniciar a transacción. Normalmente farase coa orde 'START TRANSACTION' (outros xestores teñen coma orde BEGIN TRANSACTION)
- Realizar as operacións que conforman as transaccións.
- En caso de erro en algunha das operacións anteriores executar a orde 'ROLLBACK' e saír
- En caso de que todo se execute correctamente, entón confirmamos a transacción e os cambios se gardan definitivamente na base de datos, coa orde: 'COMMIT'
Transaccións en PHP
- Para iniciar unha transacción temos dúas opcións:
- Inhabilitar as transaccións implícitas, coa función autocommit:
- Versión procedimental:
1 $conex = mysqli_connect(HOST, USER, PASSWORD, BD); 2 mysqli_autocommit($conex,false);
- Versión obxectual:
1 $conex= new mysqli(HOST, USER, PASSWORD, BD); 2 $conex->autocommit(false);
- Desta forma as operacións que vaian a continuación desta orde non se farán efectivas ata atopar un commit.
- Iniciar unha transacción coa función mysqli::begin_transaction.
- NECESITAMOS QUE A VERSIÓN PHP SEXA 5.5 OU SUPERIOR.
- Versión procedimental:
1 $conex = mysqli_connect(HOST, USER, PASSWORD, BD); 2 mysqli_begin_transaction($conex,MYSQLI_TRANS_START_READ_ONLY);
- Versión obxectual:
1 $conex= new mysqli(HOST, USER, PASSWORD, BD); 2 $conex->begin_transaction(MYSQLI_TRANS_START_READ_ONLY);
- Esta segunda forma leva como parámetro unha bandeira que vai indicar que dentro da transacción non vai poderse facer operacións que modifiquen os contidos das táboas da base de datos.
- Se a transacción necesita facer operacións contra a base de datos, levaría o flag MYSQLI_TRANS_START_READ_WRITE.
- Máis información neste enlace.
- Unha vez iniciada a transacción faremos as operacións necesarias sobre a base de datos. En caso de erro nalgunha delas, teremos que facer un rollback coa función rollback.
- Versión procedimental:
1 $conex = mysqli_connect(HOST, USER, PASSWORD, BD); 2 mysqli_begin_transaction($conex,MYSQLI_TRANS_START_READ_ONLY); 3 4 ......... 5 OPERACIÓN 6 Si erro 7 mysqli_rollback($conex); 8 die('Houbo un erro...');
- Versión obxectual:
1 $conex= new mysqli(HOST, USER, PASSWORD, BD); 2 $conex->begin_transaction(MYSQLI_TRANS_START_READ_ONLY); 3 4 ......... 5 OPERACIÓN 6 Si erro 7 $conex->rollback(); 8 die('Houbo un erro...');
- Exemplo (obxectual):
1 try { 2 $conex = new mysqli(SERVIDOR, USUARIO, PASSWORD,DB); // Constantes definidas nun arquivo fora do sitio web 3 $conex->set_charset('utf8mb4'); 4 5 $conex->begin_transaction(MYSQLI_TRANS_START_READ_WRITE); 6 $result=$conex->query("INSERT INTO TABLE_1 (col2) VALUES ('valor')"); 7 if ($result){ 8 // Operación correcta 9 printf ("Novo rexistro có id %d.\n", $conex->insert_id); 10 } 11 $result=$conex->query("INSERT INTO TABLE_2 (col2) VALUES ('valor')"); 12 if ($result){ 13 // Operación correcta 14 printf ("Novo rexistro có id %d.\n", $conex->insert_id); 15 } 16 17 } // Pecha o try da parte superior da páxina 18 catch (mysqli_sql_exception $e) { 19 if(!empty($conex)) $conex->rollback(); 20 die("Erro mysql: " . $e->getMessage()); 21 } 22 catch (Expcetion $e) { 23 if(!empty($conex)) $conex->rollback(); 24 die("Erro xeral: " . $e->getMessage()); 25 } 26 finally{ 27 if(!empty($conex)){ 28 $conex->close(); 29 } 30 }
- Unha vez rematadas as operacións facemos o commit coa función commit():
- Versión procedimental:
1 mysqli_commit($conex);
- Versión obxectual:
1 $conex->commit();
- Se establecimos autocommit(false) deberemos volvelo a establecer en True.
1 $conex->autocommit(true);
- Ao rematar todas as operacións sobre a base de datos pecharemos a conexión.
1 $conex->close();
O exemplo anterior de forma completa:
1 try { 2 $conex = new mysqli(SERVIDOR, USUARIO, PASSWORD,DB); // Constantes definidas nun arquivo fora do sitio web 3 $conex->set_charset('utf8mb4'); 4 5 $conex->begin_transaction(MYSQLI_TRANS_START_READ_WRITE); 6 $result=$conex->query("INSERT INTO TABLE_1 (col2) VALUES ('valor')"); 7 if ($result){ 8 // Operación correcta 9 printf ("Novo rexistro có id %d.\n", $conex->insert_id); 10 } 11 $result=$conex->query("INSERT INTO TABLE_2 (col2) VALUES ('valor')"); 12 if ($result){ 13 // Operación correcta 14 printf ("Novo rexistro có id %d.\n", $conex->insert_id); 15 } 16 $conex->commit(); 17 18 } // Pecha o try da parte superior da páxina 19 catch (mysqli_sql_exception $e) { 20 if(!empty($conex)) $conex->rollback(); 21 printf("Erro mysql: " . $e->getMessage()); // Se non poñemos die executa o finally. Se non habería que poñer o autocommit aquí tamén 22 23 } 24 catch (Expcetion $e) { 25 if(!empty($conex)) $conex->rollback(); 26 printf("Erro xeral: " . $e->getMessage()); // Se non poñemos die executa o finally. Se non habería que poñer o autocommit aquí tamén 27 } 28 finally{ 29 if(!empty($conex)){ 30 $conex->close(); 31 } 32 $conex->autocommit(true); 33 34 }
Transaccións en MYSQL dentro de procedementos almacenados
- Outra forma de realizar as transaccións é que poidamos enviar os datos a un procedemento almacenado de Mysql e que dende o procedemento se faga a transacción.
- O proceso é moi parecido ao feito dende PHP.
- Lembrar que se o facemos dende o propio procedemento NON TEMOS QUE FACELO dende PHP.
1 DELIMITER $$ 2 3 CREATE PROCEDURE `PROCEDEMENTO_CON_TRANSACCION`(_param1 int,_param2 varchar(50),_param3 varchar(200)) 4 BEGIN 5 DECLARE EXIT HANDLER FOR SQLEXCEPTION 6 BEGIN 7 ROLLBACK; 8 SELECT -1 as error; 9 END; 10 11 START TRANSACTION; 12 13 INSERT INTO TABLE_1(col1,col2) 14 VALUES (_param2,_param3); -- Supoñemos que esta táboa devolve un valor clave autonumérico. 15 16 INSERT INTO TABLE_2(col1,col2) 17 VALUES (_param1,last_insert_id()); 18 19 20 COMMIT; 21 SELECT 0 AS error; 22 23 END 24 $$
- Liñas 5-9: Capturamos calquera erro que se produza e nese caso enviamos a PHP un código -1 no select facendo un rollback da transacción.
- Liña 11: Iniciamos a transacción.
- Liñas 20 e 21: Confirmamos a transacción se todo foi ben e enviamos un 0 como código ao PHP.
- Comentar que se facemos as transaccións dende procedementos almacenados teremos que capturar as posibles excepcións que se produzan para facer o rollback.
- Máis información: Captura de erros en Mysql.
- A partires de Mysql 5.6 temos outra forma de capturar as excepcións: Mensaxes e códigos de erro en Mysql a partires da versión 5.6
Caso práctico: Transaccións en MYSQL dentro de procedementos almacenados
- Imos crear un procedemento almacenado que dea de alta a un único autor xunto co libro.
- Partimos da base que xa coñecemos o id do libro e enviamos o nome e apelidos do autor.
- Isto leva consigo que o procedemento vai ter que dar de alta datos en dúas táboas: AUTORES e LIBROS_AUTORES.
1 DELIMITER $$ 2 3 CREATE DEFINER=`root`@`localhost` PROCEDURE `INSERIR_AUTORES_LIBRO`(_id_libro int,_nome varchar(50),_apelido varchar(200)) 4 BEGIN 5 DECLARE EXIT HANDLER FOR SQLEXCEPTION 6 BEGIN 7 ROLLBACK; 8 SELECT -1 as error; 9 END; 10 11 START TRANSACTION; 12 13 INSERT INTO AUTORES(nome,apelido) 14 VALUES (_nome,_apelido); 15 16 INSERT INTO LIBROS_AUTORES (libro_id,autor_id) 17 VALUES (_id_libro,last_insert_id()); 18 19 COMMIT; 20 SELECT 0 AS error; 21 22 END 23 $$
Caso práctico: Transaccións
Exercicio Proposto: Asociar Libros a Autores.
- A idea é escoller dunha lista (<select>) un libro.
- Unha vez escollido e premido o botón 'AUTORES' desaparecerá a lista.
- Se indicará nun header o nome do libro e aparecerá nunha lista na que se van poder seleccionar varios autores, os autores da base de datos.
- Seleccionaremos un ou varios autores e premeremos o botón de ALTA.
- Isto levará consigo as operacións de alta sobre a táboa LIBROS_AUTORES de cada un dos autores escollidos.
- Melloras:
- Posibilidade de dar de alta a novos autores. Estes autores non aparecerán la lista ata que se dean de alta.
- Polo tanto aparecerán nunha checkboxlist con opción para ser eliminados (non da base de datos xa que aínda non foron engadidos). Poderiades gardar os datos nun array dentro dunha variable de sesión.
- Ao premer o botón de ALTA, engadiranse á base de datos e se asociarán ao libro seleccionado.
- Isto levará consigo as operacións de alta sobre a táboa AUTORES e LIBROS_AUTORES de cada un dos autores.
Exercicio proposto/resolto
- Imos crear unha páxina de nome Alta_Libro_Exemplares.php.
- Dita páxina terá un formulario onde indicaremos o título do libro e o número de exemplares que queremos dar de alta.
- Ao premer o botón de 'Alta' aparecerán en forma de lista (o podedes implementar cunha táboa) un formulario (unha por cada exemplar) cos datos: imaxe-prezo-estado.
- Os datos anteriores (título e número de exemplares) non se poderán cambiar.
- Os campos do formulario serán dos seguintes tipo:
- Estado: Será un combobox (select en html) cos estados da táboa estados.
- Imaxe: cambiaremos isto máis adiante. Por agora deixade un campo de texto.
- Prezo: Prezo da imaxe.
- Como sempre deberedes validar os datos e que teñan o tipo/formato correcto.
- No segundo formulario aparecerá un botón de 'Alta' e outro de 'Cancelar'.
- Ao premer o botón de alta daremos de alta o libro, obteremos o id autoxerado e usaremos ese id para dar de alta aos exemplares.
- Se prememos o botón 'Cancelar' desaparecerá o segundo formulario e limparase o primeiro para que poidamos dar de alta un novo libro.
- POSIBLE SOLUCIÓN (algúns dos procedementos xa foron creados en solucións anteriores)
MYSQL
Procedemento: CONSULTAR_ESTADOS
1 DELIMITER $$ 2 CREATE DEFINER=`root`@`localhost` PROCEDURE `CONSULTAR_ESTADOS`() 3 BEGIN 4 SELECT id_estado,descripcion 5 FROM ESTADOS 6 ORDER BY descripcion; 7 8 END
Procedemento: INSERIR_LIBRO
1 DELIMITER $$ 2 3 CREATE DEFINER=`root`@`localhost` PROCEDURE `INSERIR_LIBRO`(_titulo varchar(300)) 4 etiqueta:BEGIN -- Asociamos unha etiqueta ao código do procedemento para que poidamos facer algo parecido a un exit 5 declare existe int; 6 7 SELECT count(*) 8 INTO existe 9 FROM LIBROS 10 WHERE upper(titulo)=upper(_titulo); 11 12 IF existe=1 THEN 13 begin 14 SELECT -1 as valor_devolto; 15 leave etiqueta; -- Para que saia do procedemento 16 end; 17 END IF; -- Fin do IF 18 19 INSERT INTO LIBROS (titulo) 20 VALUES (_titulo); 21 22 SELECT last_insert_id() as valor_devolto; 23 END
Procedemento: INSERIR_EXEMPLAR
1 DELIMITER $$ 2 CREATE DEFINER=`root`@`localhost` PROCEDURE `INSERIR_EXEMPLAR`(_libro_id int,_id_exemplar int,_imaxe varchar(100),_prezo decimal(6,2),_estado_id int) 3 BEGIN 4 INSERT INTO EXEMPLARES (libro_id,id_exemplar,imaxe,prezo,estado_id) 5 VALUES (_libro_id,_id_exemplar,_imaxe,_prezo,_estado_id); 6 7 END 8 $$
Arquivo bd.inc.php:
1 define ('PROCEDEMENTO_INSERIR_LIBRO',"INSERIR_LIBRO('%s')"); 2 define ('PROCEDEMENTO_INSERIR_EXEMPLAR',"INSERIR_EXEMPLAR(%d,%d,'%s',%f,%d)");
Arquivo Alta_Libro_Exemplares.php
1 <?php 2 3 require ('/var/www/confBD/bd.inc.php'); 4 //error_reporting(0); 5 6 $conex = new mysqli(SERVIDOR,USUARIO,PASSWORD,BD); 7 $conex->set_charset('utf8'); 8 9 if ($conex->connect_error){ 10 die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); 11 } 12 13 $error=''; // Usado para amosar as mensaxes de erro 14 // *********************************ALTA ************************************************************************// 15 if (!empty($_POST['btnAlta'])){ // DAMOS DE ALTA A EXAMPLARES E O LIBRO 16 foreach ($_POST['lstEstado'] as $indice => $estado){ // Usamos o índice para buscar os datos no resto dos campos 17 $prezo = $_POST['txtPrezo'][$indice]; 18 $imaxe = $_POST['txtImaxe'][$indice]; 19 20 // Xa temos os datos de cada un dos exemplares 21 // Verifacamos que cumpran os criterios de tipo e formato => PRIMEIRO VALIDAMOS. SE SON CORRECTOS TODOS INICIAMOS A OPERACION DE ALTA 22 if (!filter_var($estado,FILTER_VALIDATE_INT) || $estado<0){ 23 $error .= "O estado é incorrecto..."; 24 break; 25 } 26 if (filter_var(str_replace(',', '.', $prezo),FILTER_VALIDATE_FLOAT)==false || $prezo<=0 || !preg_match("/[0-9]{1,6}(,[0-9]{1,2})?/",$prezo)){ 27 $error .= "O prezo é incorrecto..."; 28 break; 29 } 30 $imaxe=filter_var($imaxe,FILTER_SANITIZE_STRING); 31 echo strlen($imaxe); 32 if(((strlen($imaxe))>100) || (strlen($imaxe)==0)) { // O número de caracteres está establecido na táboa da base de datos 33 $error .= "O nome da imaxe é demasiado grande ou non foi posto..."; 34 break; 35 } 36 // Gardamos os datos validados para dalos de alta todos xuntos 37 $dato['prezo'] = $prezo; 38 $dato['imaxe'] = $imaxe; 39 $dato['estado'] = $estado; 40 $datos[] = $dato; 41 } // Fin do for 42 43 44 if(empty($error)){ // Non houbo error, empezamos a operación de alta con transaccións 45 $conex->autocommit(false); // Iniciamos a transcción 46 47 // Damos de alta ao libro e obtemos o id xerado 48 $titulo = $conex->real_escape_string($_POST['txtTitulo']); 49 $proc=sprintf(PROCEDEMENTO_INSERIR_LIBRO,$titulo); 50 $proc = sprintf("CALL %s",$proc); 51 if ($conex->multi_query($proc)){ 52 $result = $conex->store_result(); 53 if ($result && ($result->num_rows>0)){ 54 $fila=$result->fetch_assoc(); 55 switch($fila['valor_devolto']){ 56 case -1: 57 $error = "Ese libro xa está dado de alta na base de datos...<br />"; 58 break; 59 default: 60 $id_libro = $fila['valor_devolto']; // XA TEMOS O ID AUTONUMERICO DO LIBRO 61 } 62 $result->free(); 63 while($conex->next_result() && $conex->store_result()); 64 } 65 else{ // Sempre ten que devolver algo como temos implementado o proc. 66 $error = 'Houbo un erro na chamada á base de datos<br />'; 67 } 68 } 69 else{ 70 $error = 'Houbo un erro na chamada á base de datos <br/>'; 71 } 72 73 74 if(empty($error)){ // Non houbo erro e temos o id do libro xerado 75 // Temos que dar de alta aos exemplares 76 $cont_num_exemplar=1; // Número de exemplar 77 foreach ($datos as $dato){ 78 $prezo = $conex->real_escape_string($dato['prezo']); 79 $estado = $conex->real_escape_string($dato['estado']); 80 $imaxe = $conex->real_escape_string($dato['imaxe']); 81 $proc=sprintf(PROCEDEMENTO_INSERIR_EXEMPLAR,$id_libro,$cont_num_exemplar,$imaxe,$prezo,$estado); 82 $proc = sprintf("CALL %s",$proc); 83 if ($conex->multi_query($proc)){ 84 $result = $conex->store_result(); // Neste caso o procedemento non devolve nada => vale 'false' 85 if ($conex->errno>0){ 86 $error .= 'Houbo un erro na alta dos exemplares'; 87 break; //Saimos do for 88 } 89 while($conex->next_result() && $conex->store_result()); 90 } 91 else{ 92 $error = 'Houbo un erro na chamada á base de datos:' . $conex->error; 93 break; //Saimos do for 94 } 95 $cont_num_exemplar+=1; 96 } // Fin do For por cada exemplar 97 } 98 if (!empty($error)){ // Fallos nas operacións 99 $conex->rollback(); 100 } 101 else { // Todo correcto 102 $conex->commit(); 103 } 104 105 } // Fin de (empty($error) 106 } 107 // *********************************FIN DE ALTA ************************************************************************// 108 109 ?> 110 111 112 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 113 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 114 <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es"> 115 <head> 116 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 117 <title>Transaccións</title> 118 </head> 119 120 <body> 121 <?php 122 $titulo=''; 123 $num_exemplares=''; 124 if (!empty($_POST['btnEnviar'])) { // Prememos o botón que indica o título e o número de exemplares 125 $titulo = filter_var($_POST['txtTitulo'],FILTER_SANITIZE_STRING); 126 if (strlen($titulo)>200){ // Non pode ter unha lonxitude maior que o campo da base de datos 127 $error='A lonxitude do título non pode ser maior que 200 caracteres...<br />'; 128 } 129 $num_exemplares = $_POST['txtNumExemplares']; 130 if (!filter_var($num_exemplares,FILTER_VALIDATE_INT) || $num_exemplares > 9 || $num_exemplares < 1){ 131 $error.="O número de exemplares ten que ser numérico e non pode ser maior que 9<br />"; 132 } 133 if (strlen($titulo)==0) { 134 $error.='O título é obrigatorio...<br />'; 135 } 136 } 137 ?> 138 139 <?php 140 $inhabilitar=''; 141 if (!empty($_POST['btnEnviar']) && (empty($error))) { // Facemos que a caixa de texto e todo e demais quede inhabilitado 142 $inhabilitar="disabled='disabled'"; // Unha vez enviamos os datos do titulo e num exemplares 143 } 144 ?> 145 <form action="<?php echo htmlentities($_SERVER['PHP_SELF']) ?>" method='post'> 146 <div>Titulo Libro: <input <?php echo $inhabilitar ?> <?php printf(" value='%s'",$titulo); ?> type='text' size='100' maxlength="300" name='txtTitulo' /></div> 147 <div>Num. Exemplares: <input <?php echo $inhabilitar ?> <?php printf(" value='%s'",$num_exemplares); ?> type='number' min='1' max='9' size="1" maxlength="1" name='txtNumExemplares' /> (<10) </div> 148 <input <?php echo $inhabilitar ?> type="submit" value='Enviar' name='btnEnviar' /> 149 </form> 150 151 <?php 152 if (!empty($error)) { // En caso de atopar erros non amosa nada máis que a mensaxe 153 die("<h4>$error</h4>"); 154 } 155 ?> 156 157 158 <?php 159 if ($titulo && $num_exemplares){ // Enviamos datos 160 // NON IMOS A FACER UNHA CONSULTA A TABOA ESTADOS POR CADA UN DOS EXEMPLARES. NON TEN SENTIDO. 161 // PODERIAMOS PERCORRER O ARRAY DE RESULTADOS E AO REMATAR VOLVER AO PRINCIPIO: http://php.net/manual/es/mysqli-result.data-seek.php 162 $proc = sprintf("CALL %s",PROCEDEMENTO_CONSULTAR_ESTADOS); 163 if ($conex->multi_query($proc)){ 164 $result = $conex->store_result(); 165 if ($result && ($result->num_rows>0)){ 166 while($valor = $result->fetch_assoc()){ 167 $estados[]=$valor; 168 } 169 $result->free(); 170 while($conex->next_result() && $conex->store_result()); 171 } 172 } 173 printf("<form action=\"%s\" method='post'>",htmlentities($_SERVER['PHP_SELF'])); 174 printf("<input type='hidden' value='%s' name='txtTitulo' />",$_POST['txtTitulo']); // Como temos dous formularios, temos que gardar o texto do título nun campo oculto 175 printf ("<table>"); 176 for($cont=0;$cont<$num_exemplares;$cont++){ 177 echo "<th>Estado</th><th>Imaxe</th><th>Prezo</th>"; 178 echo "<tr>"; 179 echo "<td>"; // ESTADO 180 printf ("<select name='lstEstado[]>"); // Poñemos o nome en forma de array xa que imos recibir varios exemplares con datos. 181 foreach ($estados as $estado){ 182 printf("<option value='%d'>%s</option>",$estado['id_estado'],$estado['descripcion']); 183 } 184 printf ("</select>"); 185 echo "</td>"; 186 echo "<td>"; // IMAXE 187 printf("<input type='text' size='50' maxlength='70' name='txtImaxe[]' />"); 188 echo "</td>"; 189 echo "<td>"; // PREZO 190 printf("<input type='text' size='9' maxlength='9' name='txtPrezo[]' /> Euros (XXXXXX,YY)"); 191 echo "</td>"; 192 echo "</tr>"; 193 } 194 195 printf ("</table>"); 196 printf("<input type='submit' value='ALTA' name='btnAlta' />"); 197 printf("<input type='reset' value='CANCELAR' name='btnCancelar' />"); 198 printf("</form>"); 199 200 } 201 ?> 202 </body> 203 </html>
- Exercicio proposto
- Tedes neste enlace da WIKI a forma de subir unha imaxe ao servidor. Modificade o código anterior para facelo.
- Exercicio proposto
- Amplía a funcionalidade da páxina anterior para poder asociar a os autores cos libros na mesma operación de alta.
- Para iso fai que os autores se carguen nunha lista (<select con size en HTML>) ordenados alfabeticamente e que se poidan seleccionar varios.
- En caso de que os autores non estean dados de alta, poderás premer nun enlace que abrirá unha xanela nova (sen o toolbar, botóns,...) e que permitirá dar de alta a un novo autor. Ao pechar a xanela deberán refrescarse os datos da lista para amosar o novo autor, mantendo os datos previamente introducidos.
Variante Caso práctico: Transaccións con control de excepcións
- É o mesmo exercicio que no punto anterior, pero utilizando Excepcións dentro do bloque de instrucións que conforman a transacción.
Arquivo Alta_Libro_Exemplares.php
1 <?php 2 3 require ('/var/www/confBD/bd.inc.php'); 4 //error_reporting(0); 5 6 $conex = new mysqli(SERVIDOR,USUARIO,PASSWORD,BD); 7 $conex->set_charset('utf8'); 8 9 if ($conex->connect_error){ 10 die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); 11 } 12 13 $error=''; // Usado para amosar as mensaxes de erro 14 // *********************************ALTA ************************************************************************// 15 if (!empty($_POST['btnAlta'])){ // DAMOS DE ALTA A EXAMPLARES E O LIBRO 16 foreach ($_POST['lstEstado'] as $indice => $estado){ // Usamos o índice para buscar os datos no resto dos campos 17 $prezo = $_POST['txtPrezo'][$indice]; 18 $imaxe = $_POST['txtImaxe'][$indice]; 19 20 // Xa temos os datos de cada un dos exemplares 21 // Verifacamos que cumpran os criterios de tipo e formato => PRIMEIRO VALIDAMOS. SE SON CORRECTOS TODOS INICIAMOS A OPERACION DE ALTA 22 if (!filter_var($estado,FILTER_VALIDATE_INT) || $estado<0){ 23 $error .= "O estado é incorrecto..."; 24 break; 25 } 26 if (filter_var(str_replace(',', '.', $prezo),FILTER_VALIDATE_FLOAT)==false || $prezo<=0 || !preg_match("/[0-9]{1,6}(,[0-9]{1,2})?/",$prezo)){ 27 $error .= "O prezo é incorrecto..."; 28 break; 29 } 30 $imaxe=filter_var($imaxe,FILTER_SANITIZE_STRING); 31 if(((strlen($imaxe))>100) || (strlen($imaxe)==0)) { // O número de caracteres está establecido na táboa da base de datos 32 $error .= "O nome da imaxe é demasiado grande ou non foi posto..."; 33 break; 34 } 35 // Gardamos os datos validados para dalos de alta todos xuntos 36 $dato['prezo'] = $prezo; 37 $dato['imaxe'] = $imaxe; 38 $dato['estado'] = $estado; 39 $datos[] = $dato; 40 } // Fin do for 41 42 43 if(empty($error)){ // Non houbo error, empezamos a operación de alta con transaccións 44 $conex->autocommit(false); // Iniciamos a transcción 45 try{ 46 // Damos de alta ao libro e obtemos o id xerado 47 $titulo = $conex->real_escape_string($_POST['txtTitulo']); 48 $proc=sprintf(PROCEDEMENTO_INSERIR_LIBRO,$titulo); 49 $proc = sprintf("CALL %s",$proc); 50 if ($conex->multi_query($proc)){ 51 $result = $conex->store_result(); 52 if ($result && ($result->num_rows>0)){ 53 $fila=$result->fetch_assoc(); 54 switch($fila['valor_devolto']){ 55 case -1: 56 throw new Exception("Ese libro xa está dado de alta na base de datos...<br />", 1); // Co 1 (true) indicamos que debemos facer rollback 57 default: 58 $id_libro = $fila['valor_devolto']; // XA TEMOS O ID AUTONUMERICO DO LIBRO 59 } 60 $result->free(); 61 while($conex->next_result() && $conex->store_result()); 62 } 63 else{ // Sempre ten que devolver algo como temos implementado o proc. 64 throw new Exception('Houbo un erro na chamada á base de datos<br />',1); 65 } 66 } 67 else{ 68 throw new Exception('Houbo un erro na chamada á base de datos <br/>',1); 69 } 70 71 72 // Temos que dar de alta aos exemplares 73 $cont_num_exemplar=1; // Número de exemplar 74 foreach ($datos as $dato){ 75 $prezo = $conex->real_escape_string($dato['prezo']); 76 $estado = $conex->real_escape_string($dato['estado']); 77 $imaxe = $conex->real_escape_string($dato['imaxe']); 78 79 $proc=sprintf(PROCEDEMENTO_INSERIR_EXEMPLAR,$id_libro,$cont_num_exemplar,$imaxe,$prezo,$estado); 80 $proc = sprintf("CALL %s",$proc); 81 if ($conex->multi_query($proc)){ 82 $result = $conex->store_result(); // Neste caso o procedemento non devolve nada => vale 'false' 83 if ($conex->errno>0){ 84 throw new Exception('Houbo un erro na alta dos exemplares',1); 85 } 86 while($conex->next_result() && $conex->store_result()); 87 } 88 else{ 89 throw new Exception('Houbo un erro na chamada á base de datos:' . $conex->error,1); // Ao usuario non debemos amosarlle as mensaxes do xestor. Estamos na fase de desenvolvemento 90 } 91 $cont_num_exemplar+=1; 92 } // Fin do For por cada exemplar 93 94 95 $conex->commit(); // Todo correcto, confirmamos a transacción 96 $error="Libro e examplares dados de alta correctamente..."; 97 } catch (Exception $ex) { 98 if ($ex->getCode()==1){ 99 $conex->rollback(); 100 } 101 $error = $ex->getMessage(); 102 103 } 104 105 106 } // Fin de (empty($error) 107 } 108 // *********************************FIN DE ALTA ************************************************************************// 109 110 ?> 111 112 113 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 114 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 115 <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es"> 116 <head> 117 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 118 <title>Transaccións</title> 119 </head> 120 121 <body> 122 <?php 123 $titulo=''; 124 $num_exemplares=''; 125 if (!empty($_POST['btnEnviar'])) { // Prememos o botón que indica o título e o número de exemplares 126 $titulo = filter_var($_POST['txtTitulo'],FILTER_SANITIZE_STRING); 127 if (strlen($titulo)>200){ // Non pode ter unha lonxitude maior que o campo da base de datos 128 $error='A lonxitude do título non pode ser maior que 200 caracteres...<br />'; 129 } 130 $num_exemplares = $_POST['txtNumExemplares']; 131 if (!filter_var($num_exemplares,FILTER_VALIDATE_INT) || $num_exemplares > 9 || $num_exemplares < 1){ 132 $error.="O número de exemplares ten que ser numérico e non pode ser maior que 9<br />"; 133 } 134 if (strlen($titulo)==0) { 135 $error.='O título é obrigatorio...<br />'; 136 } 137 } 138 ?> 139 140 <?php 141 $inhabilitar=''; 142 if (!empty($_POST['btnEnviar']) && (empty($error))) { // Facemos que a caixa de texto e todo e demais quede inhabilitado 143 $inhabilitar="disabled='disabled'"; // Unha vez enviamos os datos do titulo e num exemplares 144 } 145 ?> 146 <form action="<?php echo htmlentities($_SERVER['PHP_SELF']) ?>" method='post'> 147 <div>Titulo Libro: <input <?php echo $inhabilitar ?> <?php printf(" value='%s'",$titulo); ?> type='text' size='100' maxlength="300" name='txtTitulo' /></div> 148 <div>Num. Exemplares: <input <?php echo $inhabilitar ?> <?php printf(" value='%s'",$num_exemplares); ?> type='number' min='1' max='9' size="1" maxlength="1" name='txtNumExemplares' /> (<10) </div> 149 <input <?php echo $inhabilitar ?> type="submit" value='Enviar' name='btnEnviar' /> 150 </form> 151 152 <?php 153 if (!empty($error)) { // En caso de atopar erros non amosa nada máis que a mensaxe 154 die("<h4>$error</h4>"); 155 } 156 ?> 157 158 159 <?php 160 if ($titulo && $num_exemplares){ // Enviamos datos 161 // NON IMOS A FACER UNHA CONSULTA A TABOA ESTADOS POR CADA UN DOS EXEMPLARES. NON TEN SENTIDO. 162 // PODERIAMOS PERCORRER O ARRAY DE RESULTADOS E AO REMATAR VOLVER AO PRINCIPIO: http://php.net/manual/es/mysqli-result.data-seek.php 163 $proc = sprintf("CALL %s",PROCEDEMENTO_CONSULTAR_ESTADOS); 164 if ($conex->multi_query($proc)){ 165 $result = $conex->store_result(); 166 if ($result && ($result->num_rows>0)){ 167 while($valor = $result->fetch_assoc()){ 168 $estados[]=$valor; 169 } 170 $result->free(); 171 while($conex->next_result() && $conex->store_result()); 172 } 173 } 174 printf("<form action=\"%s\" method='post'>",htmlentities($_SERVER['PHP_SELF'])); 175 printf("<input type='hidden' value='%s' name='txtTitulo' />",$_POST['txtTitulo']); // Como temos dous formularios, temos que gardar o texto do título nun campo oculto 176 printf ("<table>"); 177 for($cont=0;$cont<$num_exemplares;$cont++){ 178 echo "<th>Estado</th><th>Imaxe</th><th>Prezo</th>"; 179 echo "<tr>"; 180 echo "<td>"; // ESTADO 181 printf ("<select name='lstEstado[]>"); // Poñemos o nome en forma de array xa que imos recibir varios exemplares con datos. 182 foreach ($estados as $estado){ 183 printf("<option value='%d'>%s</option>",$estado['id_estado'],$estado['descripcion']); 184 } 185 printf ("</select>"); 186 echo "</td>"; 187 echo "<td>"; // IMAXE 188 printf("<input type='text' size='50' maxlength='70' name='txtImaxe[]' />"); 189 echo "</td>"; 190 echo "<td>"; // PREZO 191 printf("<input type='text' size='9' maxlength='9' name='txtPrezo[]' /> Euros (XXXXXX,YY)"); 192 echo "</td>"; 193 echo "</tr>"; 194 } 195 196 printf ("</table>"); 197 printf("<input type='submit' value='ALTA' name='btnAlta' />"); 198 printf("<input type='reset' value='CANCELAR' name='btnCancelar' />"); 199 printf("</form>"); 200 201 } 202 ?> 203 </body> 204 </html>
Permisos mínimos
- Sempre debemos ter en mente a seguridade de acceso aos datos gardados nunha base de datos.
- Ata o de agora, o usuario que usamos para acceder á base de datos tiña asinado todos os permisos sobre a mesma, pero esta práctica non é aconsellable.
- O ideal é que teña asinado os permisos mínimos necesarios para que poida utilizar a aplicación.
- Cando creamos unha aplicación WEB podemos ter varios supostos no que se refire ao acceso á base de datos.
- Temos creado en MYSQL un usuario diferente por cada usuario, de tal forma que a cadea de conexión á base de datos a crearemos en base ao usuario/password introducido dende unha páxina PHP.
- Utilizamos o mesmo usuario MYSQL para acceder á base de datos e temos unha táboa de usuario/password. En función do usuario validado, este terá acceso a diferentes páxinas PHP con diferentes funcionalidades.
- Utilizamos o mesmo usuario MYSQL para acceder á base de datos e todos os usuarios do sitio web poden ter acceso ás mesmas páxinas.
- En calquera destes supostos, o usuario que empreguemos para acceder a MYSQL debe ter os permisos mínimos para poder funcionar no noso sitio web.
- Partimos de que temos creado un usuario en Mysql, que vai ser o usuario que imos utilizar no noso sitio web para conectarnos a MYSQL e queremos darlle os permisos mínimos.
- CASO 1: A nosa aplicación fai as operacións contra a base de datos empregando sentenzas SQL (SELECT/INSERT/UPDATE/DELETE) deberemos outorgar os permisos e so os permisos necesarios para facer as operacións sobre as táboas da nosa base de datos.
- Para facelo deberemos facer uso da orde grant e orde revoke.
- A primeira (grant) serve para outorgar un permiso a un usuario.
- A segunda (revoke) serve para quitar un permiso a un usuario.
- No caso de SELECT e UPDATE, podemos outorgar permisos a nivel de columnas, de tal forma que podemos especificar que columnas dunha táboa vai ter permiso para poder ser seleccionadas ou actualizadas.
- Se estamos a utilizar o WorkBench, podemos asociar os permisos graficamente.
- Prememos na sección 'User and privileges' (1).
- Prememos o botón 'Add entry' e escollemos o schema (base de datos) sobre a que queremos outorgar os permisos (2).
- Aparecerá o schema na lista (3)^.
- Agora na parte baixa aparecerán os permisos que podemos outorgar. Fixarse que os marcados con (4) son os permisos de SELECT/INSERT/UPDATE/DELETE
- Se eliximos este 'tipo de permisos' estaremos dando o permiso sobre todas as táboas creadas na base de datos.
- Se estamos a utilizar a ferramenta Phpmyadmin poderemos facer o mesmo seleccionado a base de datos e indo a opción Privilegios. Aparecerá o usuario creado e poderemos editar os seus permisos.
- Se queremos dar un permiso individual a unha táboa usaremos a orde grant. Esta será a opción a escoller se queremos otorgar un permisos mínimos a nosa base de datos:
- Nota: A orde grant, cando fai referencia a un obxecto (coma unha táboa) pode indicarse o nome da base de datos e o nome do obxecto desta forma: bd.obxecto
- Nos exemplos seguintes omitimos o nome da base de datos e supoñemos que xa está seleccionada (activa) a base de datos sobre a que imos a executar as ordes.
- Permiso 'select' sobre unha táboa.
1 grant select on TABOA to 'novo_usuario';
- Permiso 'select' sobre unha columna dunha táboa.
1 grant select (columna) on TABOA to 'novo_usuario';
- Permiso 'update' sobre unha táboa.
1 grant update on TABOA to 'novo_usuario';
- Permiso 'update' sobre unha columna dunha táboa.
1 grant update (columna) on TABOA to 'novo_usuario';
- Para os permisos DELETE e INSERT soamente é necesario poñer na orde grant a operación igual que nos casos anteriores.
- CASO 2: A nosa aplicación fai as operacións contra a base de datos empregando procedementos almacenados.
- No caso anterior, aínda que melloramos a seguridade, o usuario aínda ten acceso ás táboas directamente e pode ver a estrutura das mesmas.
- Se queremos aumentar a seguridade podemos utilizar procedementos almacenados.
- Xa vimos anteriormente que empregar ditos procedementos leva consigo:
- Mellor rendemento xa que as sentenzas xa están compiladas e analizadas polo xestor.
- Mellor seguridade xa que é imposible facer ataques de inxección de código.
- Mellor seguridade xa que o usuario non pode ver a estrutura de táboas.
- Mantemento máis doado, xa que podo cambiar a estrutura de táboas sempre que o procedemento continúe devolvendo o mesmo ao usuario.
- IMPORTANTE: Se utilizamos este tipo de 'seguridade' xa non necesitamos outorgar ningún tipo de permiso sobre as táboas.
- O permiso que debemos outorgar para que poida executar un procedemento almacenado é 'EXECUTE'.
- Podemos facelo como no punto anterior facendo uso do WORKBENCH (neste caso daremos permiso de execución sobre todos os procedementos almacenados).
- Usar a orde grant:
- Dando permiso de execución a todos os procedementos almacenados.
1 grant execute on *.* to 'novo_usuario'
- Dando permiso de execución a un procedemento almacenado.
1 grant execute on procedure NOME_PROCEDEMENTO to 'novo_usuario'
Optimización: Sentenzas Prepared
- A idea das sentenzas 'prepared' é a de mandar previamente ao Mysql a sentenza que imos executar pero sen datos.
- Despois asociaremos a dita sentenza 'prepared' os datos e a executaremos.
- Por exemplo:
1 select col1 from TABOA1 2 where col2=?
- Neste caso, a sentenza a executar leva un parámetro (o indicado pola interrogación).
- Este parámetro se asociará posteriormente e se executará a sentenza. Podemos executar a sentenza moitas veces cambiando o valor do parámetro.
- Preparar as sentenzas ten as vantaxes:
- É máis rápido que enviar as sentenzas da forma 'tradicional' sobre todo cando repetimos a mesma sentenza (con valores diferentes) varias veces. Isto é debido a que cando 'preparamos' a sentenza a estamos a enviar ao xestor MYSQL o cal xa fai as operacións de análise da sentenza e outras optimizacións. Isto soamente o terá que facer unha vez.
- Maior seguridade, xa que fai imposible a inxección de código (o mesmo que pasa cos procedementos almacenados) como vimos anteriormente nesta wiki.
PHP
MySQL
- Dende o propio xestor tamén se poden 'executar' sentenzas 'prepared'.
- Esta forma pode sernos moi útil se queremos executar sentenzas SQL que se cren dinamicamente dentro dun procedemento almacenado.
- Por exemplo, se temos unha páxina de busca en base a varios criterios (por exemplo, nome e idade). A sentenza SQL terá unha parte WHERE que pode ser:
- nome=valor1
- idade=valor2
- nome=valor1 and idade=valor2
- Vai depender se enviamos ou non o dato teremos que executar unha instrución ou outra.
- En vez de ter 3 instrucións SQL dentro do procedemento almacenado en base a se enviamos ou non o dato, podemos 'construír' a sentenza SQL coma unha cadea e cando a teñamos executala con 'PREPARED'
1 DELIMITER $$ 2 3 CREATE DEFINER=`root`@`localhost` PROCEDURE `proba`(_apelido varchar(100),_nome varchar(50)) 4 BEGIN 5 6 set @cadea = 'select nome,apelido from AUTORES where 1=1'; 7 if (_apelido is not null) then 8 set @cadea=concat(@cadea,' and apelido=','\'', _apelido,'\''); 9 end if; 10 if (_nome is not null) then 11 set @cadea=concat(@cadea,' and nome=','\'', _nome,'\''); 12 end if; 13 14 prepare stmt from @cadea; 15 16 execute stmt; 17 18 deallocate prepare stmt; 19 20 21 END
- Neste caso o procedemento buscará nunha táboa AUTORES por un nome, apelido ou nome e apelido concreto. En caso de enviar null nos dous parámetros, seleccionará todas as filas.
- Fixarse nos espazos en branco cando concatenedes xa que tedes que formar a frase: WHERE 1=1 and cond=valor. Entre o 1=1 e a condición ten que ir un espazo en branco e o mesmo se engadides máis condicións.
Gardando campos a protexer na base de datos
- En moitas ocasións imos necesitar gardar nunha base de datos información que non queremos que sexa 'visible' en caso de que alguén poida acceder a nosa base de datos.
- Un exemplo típico é o das contrasinais dos usuarios do noso sitio web, no caso de implantar un sitio web cunha validación de usuarios.
- Información sobre a seguridade das passwords.
- Como normas, nunca debe estar baseada nun dicionario.
- Debe levar caracteres alfanuméricos, maiúsculas e minúsculas e algún carácter non alfanumérico.
- Debe ser de a lo menos 10 ou máis caracteres
- Para gardar a información dunha forma segura temos que aplicar un algoritmo de hash.
- En PHP temos dispoñible a partires da versión 5, a función de hash.
1 <?php 2 echo hash('ripemd160', 'The quick brown fox jumped over the lazy dog.'); 3 ?>
- Dará como resultado:
1 ec457d0a974c48d5685a7efa03d137dc8bbde7e3
- En PHP temos funcións de hash específicas dun tipo de algoritmo. Por exemplo a función md5.
1 <?php 2 echo md5('The quick brown fox jumped over the lazy dog.') // As dúas funcións devolven o mesmo 3 echo hash('md5', 'The quick brown fox jumped over the lazy dog.'); // As dúas funcións devolven o mesmo 4 ?>
- Pero esta forma de 'tratar' a información non chega, xa que se podería descubrir o valor orixinal pola forza bruta.
- Para complicalo un pouco máis debemos facer uso de SALT'.
- Salt é unha cadea que se aplica no proceso de hash e fai que sexa practicamente imposible utilizar mecanismos de forza bruta ou Táboas Rainbow para obter a cadea orixinal.
- Exemplo de como utilizar salt na función md5.
1 <?php 2 $password='q121ahgsha..'; // Password introducida polo usuario. Viría dun formulario con $_POST['txtPassword']. 3 $cadeaSalt='unha cadea ao chou'; 4 5 echo md5($cadeaSalt,$password) 6 ?>
- Cabe sinalar que se a contrasinal é débil, é posible chegar a rompela, polo que é moi importante que a contrasinal sexa 'forte'.
- A cadea SALT pode ser calquera cadea e mellor que non estea baseada en ningún dicionario.
Opción recomendada
- A cadea SALT a podemos proporcionar nos ou podemos xerar unha por medio da función password_hash.
1 $password = password_hash('contrasinal_a_codificar', PASSWORD_DEFAULT);
- Codifica a contrasinal utilizando o algoritmo bcrypt e o resultado é unha cadea de 60 caracteres de lonxitude. Seguramente en versións posteriores, o algoritmo vai devolve lonxitudes máis grandes, polo que é recomendable gardar a contrasinal nun campo de 256 caracteres de lonxitede.
- Esta forma de codificar fai que o valor SALT cambie de cada vez.
- O resultado será o valor que teremos que gardar na base de datos.
- Para realizar o proceso contrario, e comprobar se o password introducido por un usuario se corresponde con o gardado na base de datos, teremos que facer uso da función password_verify().
1 $orixinal = 'password_introdicada_polo_usuario'; 2 $codificado = '2y$10$ETpCoo11Gd3w2kx4x1me1OQtx.p67c6eRzg6OSRgPxUsBGaQnff4W'; 3 4 $iguais = password_verify($orixinal, $codificado); 5 6 if ($iguais) { 7 echo 'Password correcto'; 8 } else { 9 echo 'Password incorrecto'; 10 }
- Máis información neste enlace.
Subindo unha imaxe ao servidor
- Lembrar que normalmente o que subimos á base de datos é a ruta 'relativa' onde se gardan as imaxes no servidor.
- A forma para gardar 'fisicamente' a imaxe no servidor xa a vimos anteriormente neste manual.
-- Ángel D. Fernández González -- (2017).
