- ¿Qué es PHP?
- XAMPP
- Sintaxsis de PHP
- Variables
- Tipos de datos
- Arreglos
- Condicionales
- Ciclos
- Operadores
- Continue
- Break
- Funciones
- Agregando archivos externos
- Programación Orientada a Objetos
- Interfaces
- Namespaces
- PSR y PHPFIG
- Composer
- Base de Datos
- ORM
- Formularios
- Eloquent
- Front Controller
- Request
- Router
- MVC
- Template engines
- Subida de Archivos
- Validaciones
- Autenticar Usuarios
- Redirigir a otra página
- Sesiones
- Restringir acceso a usuarios autenticados
- Variables de Entorno
- Deploy
- Enlaces de Interés
PHP es un lenguaje de programación de propósito general de alto nivel que fue diseñado especialmente para el desarrollo de aplicaciones web.
- Es un lenguaje interpretado, necesitas un interprete de php en la pc para ejecutarlo.
- Es un lenguaje multiplataforma (Win, linux, MacOS).
- Es open source, el codigo fuente esta disponible!
- Tienes muchas integraciones.
¿Qué NO es PHP?
- No es un lenguaje compilado, por lo cual siempre tendrás que llevar juntos tu código y tu interprete.
- No esta diseñado para realizar aplicaciones de escritorio.
- Para trabajar con PHP instalaremos un entorno de desarrollo llamado XAMPP, no es un entorno pensado para producción.
Es un entorno para poder desarrollar en PHP.
- X: Cualquier sistema operativo.
- A: Apache
- M: MariaBD
- P: PHP/Perl
Para instalar XAMPP solo hay que ir a la página de Apache y descargar el instalador.
Al finalizar la instalación, abrir XAMPP e iniciar el servidor de Apache y MySQL.
Si al iniciar el servidor de Apache sale un error por que el puerto está siendo usado, se puede cambiar de la siguiente manera:
- Click en Config de Apache.
- Click en httpd.conf.
- Cambiar Listen 80
- Cambiar ServerName localhost:80
Siempre que usemos PHP usaremos lo siguiente:
<?php
echo 'hello PHP';
?>
Todo lo que pongamos dentro de esto será lo que el servidor va a interpretar como código php, lo que esté fuera lo ignorará.
Si queremos escribir código php en nuestra vista HTML tendremos que cambiarle la extensión al archivo por .php porque nuestro servidor esta configurado a solo interpretar archivos PHP. Solo las partes dentro de <?php ?>
van a ser interpretadas y su código fuente no será visible desde el navegador.
Todas las sentencias de código se separarán con un ;
(punto y coma).
Una variable puede ser una pequeña cajita en la que puedes almacenar un valor y este lo pueden usar para realizar alguna operación.
Para declararla usaremos el símbolo de $ y en seguida el nombre, este puede ser un _ o una letra.
<?php
$number = 1;
$name = 'Sergio';
?>
PHP no es estáticamente tipado, es decir que no tenemos que decirle qué tipo de dato es esa variable. Además, es débilmente tipado porque podemos fácilmente cambiar el tipo de dato, es decir PHP ejecuta una conversión de datos interna.
Al momento de trabajar con PHP una cosa muy importante es hacer debugging a nuestras variables, para ello utilizamos la función var_dump();
pasándole por parámetro la variable a revisar.
<?php
$name = 'Sergio';
var_dump($name)
?>
En PHP tenemos dos tipos de cadenas, las que son con comillas simples y las de comillas dobles. La diferencia entre estas dos cadenas es que la de comillas simples recibe de forma literal lo que le escribas mientras que la de comillas dobles intenta interpretar cualquier variable dentro de ella.
<?php
$name = 'Sergio';
//Comillas Dobles
$hello = "Hola $name";
//Comillas Simples
$hello2 = 'Hola ' . $name;
?>
PHP cuenta con muchos tipos de datos, sin embargo, en este momento nos vamos a enfocar en los más importantes y utilizados que son boolean, integer, float, string, array y NULL.
Representa solamente un valor verdadero o falso.
Si quieres conocer más de este tipo de dato da click aquí
Valores válidos: true (verdadero) false (falso)
<?php
$a = true;
$b = false;
?>
Representa un número entero positivo, negativo o 0.
Si quieres conocer más de este tipo de dato da click aquí
<?php
$a = -123;
$b = 0;
$c = 7763;
?>
Representa un número de punto flotante, existen problemas de precisión con los números flotantes debido a la naturaleza binaria de las computadoras.
Si quieres conocer más de este tipo de dato da click aquí
<?php
$a = 12.24;
$b = 1.5e3;
$c = 7E-10;
?>
- Representa una cadena de caracteres.
- Existen 4 formas de representar una cadena.
Las 2 principales son usando comillas simples o comillas dobles.
- Usando comillas simples donde el texto será exactamente como se escribe.
- Usando comillas dobles permite usar caracteres de escape y además expanden los nombres de las variables, es decir sustituye el valor de las variables dentro de las cadenas.
Hay 2 formas adicionales llamadas Heredoc y Nowdoc que sirven para crear cadenas de múltiples líneas.
Si quieres conocer más de este tipo de dato da click aquí.
<?php
$a = "Hola";
$b = 'Mundo';
?>
Representa una colección de valores, aunque por defecto PHP usara índices numéricos, la realidad es que la estructura se representa como un mapa que colecciona pares llave-valor. La sintaxis para definir un arreglo será a partir de corchetes cuadrados, aunque en versiones anteriores de PHP era necesario usar la función array(). Las llaves pueden ser enteros o cadenas y los valores pueden ser de cualquier tipo de PHP, incluso de tipo array.
Si quieres conocer más de este tipo de dato da click aquí
<?php
$array = array(
"curso1" => "php",
"curso2" => "js",
);
// a partir de PHP 5.4
$array = [
"curso1" => "php",
"curso2" => "js",
];
// índices numéricos
$array = [
"php",
"js",
];
?>
Representa una instancia de una clase. Este tema lo veremos más a fondo en la clase de Programación Orientada a Objetos.
<?php
class Car
{
function move()
{
echo "Going forward...";
}
}
$myCar = new Car();
$myCar->move();
?>
Es un tipo de dato especial que representa a algo que puede ser “llamado”, por ejemplo una función o un método.
<?php
// Variable que guarda un callable
$firstOfArray = function(array $array) {
if (count($array) == 0) { return null; }
return $array[0];
};
// Este es nuestro arreglo
$values = [3, 2, 1];
// Usamos nuestro callable y se imprime el valor 3
echo $firstOfArray($values);
?>
A partir de PHP 7.1 iterable es un pseudo tipo de datos que puede ser recorrido.
<?php
function foo(iterable $iterable) {
foreach ($iterable as $valor) {
// ...
}
}
?>
Es un tipo de dato especial que representa un recurso externo, por ejemplo un archivo externo a tu aplicación.
<?php
$res = fopen("c:\\dir\\file.txt", "r");
?>
Es un valor especial que se usa para representar una variable sin valor.
Si quieres conocer más de este tipo de dato da click aquí
<?php
$a = null;
?>
Las variables que almacenan más de un dato se conocen como arreglos y su sintaxis se va a indicar con [ ] (corchetes).
<?php
$jobs = [
'PHP Dev',
'Python Dev'.
'Devops'
];
?>
PHP utiliza índices para localizar a los elementos dentro de la variable. Los índices empiezan de cero.
<?php
echo $jobs[0];
// imprime PHP Dev
?>
La estructura de arreglos en PHP es conocida como mapa, lo que quiere decir que tiene una composición de llave valor. Además, un arreglo puede contener más arreglos y cada uno de ellos seguirá la misma estructura.
<?php
$jobs = [
[
'title' => 'PHP Dev',
],
[
'title' => 'Python Dev'.
],
[
'title' => 'Devops'
]
];
// imprime PHP Dev
echo $jobs[0]['title'];
?>
Algo que debes saber es que en PHP podrás almacenar diferentes tipos de datos en un mismo arreglo.
Las condiciones nos permiten tomar decisiones en el código, si se cumple la condición entonces se ejecutarán ciertas instrucciones sino se cumple se ejecutarán otras. Estas se denotan por la instrucción if else.
$var1 = 1;
if($var1 > 0) {
echo 'es mayor que 2';
}
else {
echo 'no es mayor que 2';
}
Los ciclos o bucles son de total importancia cuando desarrollamos software pues nos permiten repetir un bloque de acciones y en consecuencia re-utilizar mejor nuestro código.
$idx = 0;
do {
echo $idx;
$idx++;
} while($idx < 3);
El ciclo do while garantiza que el código interno se ejecutará al menos 1 vez.
$idx = 0;
while ($idx < 3) {
echo $idx;
$idx++;
}
En el ciclo while si la condición es falsa desde un inicio, es posible que el ciclo nunca se ejecute.
for($idx = 0; $idx < 3; $idx++) {
echo $idx;
}
El ciclo foreach nos brinda una solución simple para iterar sobre los valores de un arreglo, la sintaxis es la siguiente:
$array = ['uno', 'dos', 'tres']
foreach ($array as $valor) {
echo $valor;
}
En esta sintaxis nos encontramos con 4 partes:
- La palabra reservada foreach simplemente indica el inicio de nuestro bloque.
- Dentro de paréntesis se escribe el nombre del arreglo que vamos a estar iterando, este arreglo debe estar definido previamente, en este ejemplo es $arreglo.
- La palabra "as" seguido de un nombre de variable que usaremos para acceder al elemento del arreglo que estamos accediendo, esta variable no debe existir previamente y solo la podrán usar dentro de este bloque. En el ejemplo es $valor.
- Entre llaves "{ }" están todas las acciones que queremos repetir, en el momento en que se ejecute el ciclo la variable que definimos para iterar (en el ejemplo $valor) ya existe y podrá ser usada en esta sección, piensa que el valor de esta variable estará cambiando en cada iteración.
Suponiendo que en el ejemplo anterior, el ciclo se repetirá 3 veces y en cada iteración la variable $valor contendrá el elemento del arreglo correspondiente, es decir, en la primera iteración $valor será igual a ‘uno’, en la segunda $valor será igual a ‘dos’ y en la tercera $valor será igual a ‘tres’.
Existe una sintaxis alternativa que nos permite no solo conocer el valor, también nos permitirá conocer la llave, de este modo tendremos acceso tanto a la llave como al valor del elemento del arreglo:
foreach ($array as $llave => $valor) {
//sentencias que pueden usar $llave y $valor
}
Funcionan para realizar operaciones aritméticas.
El operador principal de asignación es el símbolo = (igual). Es importante tener en cuenta que este operador no sirve para comparar, normalmente del lado izquierdo del operador tendremos una variable, y este operador sirve para asignar el resultado de lo que se encuentre a la derecha a dicha variable.
$variable = 5;
Lo que tenemos en la derecha puede ser un valor, otra variable, o el resultado de una operación o función.
También existen otros operadores de asignación que se combinan con operadores aritméticos o para strings y nos permiten simplificar algunas sentencias dentro de PHP. Estos son ejemplos de cómo funcionan:
$a += $b
$a = $a + $b
$a -= $b
$a = $a - $b
$a *= $b
$a = $a * $b
$a /= $b
$a = $a / $b
$a %= $b
$a = $a % $b
$a .= $b
$a = $a . $b
Nos permiten comparar valores.
Permiten incrementar o decrementar un valor en 1.
Es muy importante entender cómo afecta el lugar donde se establece el operador, ejemplo:
$a = 1;
echo $a++;
echo $a;
echo ++$a;
echo $a;
imprime:
1
2
3
3
Nos permiten combinar resultados de comparaciones.
Existen 2 operadores para strings el . (punto) que nos permite concatenar cadenas, y el .= que ya fue visto anteriormente y nos permite simplificar la sintaxis de concatenar algo a una misma cadena, ejemplo:
$var1 = ‘Hola ’ . ‘ php’;
$var1 .= ‘!!!’;
echo $var1;
imprime
Hola php!!!
La sentencia continue
hará que se itere a la siguiente línea del arreglo.
for($idx = 0; $idx < 10; $idx++) {
if($continuar) {
continue;
}
}
Si se ejecuta continue, entonces se pasa a la siguiente iteración del for.
Y la sentencia break que hará que el ciclo se termine.
La sentencia break hace que el ciclo se termine.
for($idx = 0; $idx < 10; $idx++) {
if($saltar) {
break;
}
}
Si se actiba el break, entonces se termina el ciclo for.
Las funciones en PHP se denotan por la palabra reservada function seguida por el nombre de la función, las funciones nos servirán para llamar y reutilizar código en nuestros proyectos.
Cuando trabajemos con funciones es muy importante cuidar el scope (alcance) de las variables pues, algunas podrían entrar en su scope y otras no.
Las funciones en PHP pueden o no regresar un dato particular. Si deseamos hacerlo podemos indicarlo con la palabra reservada return.
function printJob() {
//Contenido de la función
}
Podemos usar archivos de la siguiente manera:
1. include
Include hace que un archivo externo se agregue dentro del archivo que estamos usando.
include('jobs.php');
Si el archivo no se encuentra, entonces mostrará un warnning.
2. require
A diferencia de include, require muestra un error si no encuentra en archivo.
require('jobs.php');
3. include_once
Es como include, pero solo agrega el archivo una sola vez independiente de las veces que se agregue.
include_once('jobs.php');
3. require_once
Es como require, pero solo agrega el archivo una sola vez independiente de las veces que se agregue.
require_once('jobs.php');
La programación orientada a objetos nos ayudará a estructurar mejor nuestros programas. PHP, a partir de su versión 5, tiene implementaciones orientadas a objetos, lo que lo hace tener código más reutilizable y mantenible.
Una clase es una plantilla o definición de algo. Y una instancia es la representación concreta de la clase.
Encapsulamiento será el nivel de visibilidad que queramos darle a alguna variable, para ello podemos utilizar los modificadores de acceso private, public y protected.
- Public: Todos pueden tener acceso.
- Private: El acceso es solo dentro de la clase.
- Protected: El acceso es solo dentro de la clase y los que heredan de la clase.
Con la palabra reservada this estaremos haciendo referencia a la variable que pertenece a la clase.
class Job {
private $title;
public $description;
public $visible;
public $months;
public function setTitle($title) {
$this->title = $title;
}
public function getTitle() {
return $this->title;
}
}
Para instanciar una clase se haría de la siguiente manera:
$job1 = new Job();
$job1->setTitle('PHP Developer');
$job1->description = 'This is an awesome job';
$job1->visible = true;
$job1->months = 16;
El método constructor es una clase que se llama al instanciar una clase.
class Job {
private $title;
public $description;
public function __construct($title, $description) {
$this->setTitle($title);
$this->description = $description;
}
}
$job = new Job('título', 'descripcion');
La herencia permite que ciertas clases tengan características de una clase padre. Esta clase se llamará hijo.
Es muy conveniente utilizar require_once cuando queremos utilizar herencia e incluir clases que están en otros archivos.
//BaseElement.php
class BaseElement {
private $title;
public $description;
public $visible = true;
public $months;
}
De este modo se declare una clase hijo.
//Job.php
require_once('BaseElement.php');
class Job extends BaseElement {
}
La herencia en PHP será de forma sencilla es decir solo que podrá hacer herencia de una sola clase.
Dentro de nuestra clase hijo podemos sobrescribir algún método del padre, esto es un concepto que conocemos como polimorfismo. Lo que polimorfismo quiere decir es que tendremos un método que va a funcionar de acuerdo con su contexto donde es llamado.
class BaseElement {
public function getText() {
return "Text";
}
}
class Job extends BaseElement {
public function getText() {
return "Text from jobs";
}
}
De este modo, cuando se instancie un objeto con la clase Job y se llame al método getText(), entonces se usa el método de la clase Job.
Si se desea acceder al constructor del padre desde la clase hijo, se puede hacer de la aiguiente forma:
public function __construct($title, $description) {
parent::__construct($newTitle, $description);
}
Las interfaces se pueden ver como un contrato o un acuerdo en el que se pueden estandarizar ciertas cosas.
La palabra reservada que utilizaremos para declarar una interfaz será interface. Y la que nos indicará que estamos usando una interfaz en una clase será implements.
interface Printable {
public function getDescription();
}
Para usar la interface, se ha de la siguiente forma:
require_once('Printable.php');
class BaseElement implements Printable {
public function getDescription() {
return $this->description;
}
}
Usando Type Hinting estableceremos el tipo de dato que esperamos ya sea una clase o un tipo de dato específico.
function printDescription(Printable $job) {
echo $job->getDescription();
}
En el caso de las interfaces, sí podemos implementar varias al mismo tiempo.
Esta es una forma de mantener únicos los nombres de los archivos en el mismo directorio.
Esto nos permite tener mejor organizado el proyecto.
Para declarar un espacio de nombres privado se utiliza la palabra reservada namespace.
namespace App\Models;
class Project {
}
Un standard es usar namespaces conforme a la estructura de carpetas en la que se encuentran los archivos.
Para usar un archivo de otro namespace, se puede hacer de la siguiente forma:
use App\Models\Job;
use App\Models\Project;
use App\Models\Printable;
//A partir de php7
use App\Models\{Job, Project, Printable};
Otra forma de hacerlo es al siguiente:
$project = new Lib\Project();
Varios programadores se unieron para crear un grupo llamado PHP-FIG con el objetivo de avanzar en la interoperabilidad de librerías en PHP.
Este grupo creo el PSR que son recomendaciones y estándares para tu código de PHP.
Composer es un manejador de dependencias de PHP que no solo nos ayudara a traer librerías de tercero al proyecto, sino que además va a implementar el estándar PSR4 que nos va a permitir tener el cargado de archivos automático.
composer.phar será un documento que nos servirá para manejar las dependencias en PHP, esta va muy de la mano con otro archivo llamado composer.json.
El composer.phar se va a descargar y pegar en el directorio del proyecto mientras que composer.json sigue la siguiente estructura:
{
"autoload": {
"psr-4": {
"App\\": "app/"
}
},
"require": {}
}
Dentro de psr-4 se van a agregar los namespaces que se van a manejar dentro de la aplicación.
Luego de tener los dos archivos, ir al directorio del proyecto en la terminal y escribir:
$ php composer.phar install
Al hacer esto se va a crear una carpeta vendor. En esta carpeta se van a agregar los namespaces definidos en composer.json.
Luego de hacer esto, hay que hacer un require_once al archivo vendor/autoload.php y con esto ya se va a activar el autoload.
require_once('vendor/autoload.php');
Esta funcionalidad va a ser que ya no sea necesario hacer un require de los archivos que se encuentren en los namespaces definidos en composer.json.
Las bases de datos son colecciones de datos que podemos usar para consultarla almacenarla, ejecutar filtros, etc.
Cuando hablamos de aplicaciones web trabajaremos con sistemas manejadores de bases de datos, o también conocidos como bases de datos relacionales.
XAMPP viene con una herramienta llamada phpMyAdmin que es un cliente el cual se conecta a una base de datos, en este caso se conecta a MariaDB.
Un ORM, que significa Object Relational Mapping, es un concepto en el cual vamos a crear dentro de nuestro código algunos modelos basados en las tablas de nuestra base de datos.
Una principal característica de un ORM es que hace más transparente las conexiones a PostgreSQL y MySQL, además nos protege de algunas vulnerabilidades de SQL y facilita algunas validaciones a la información.
Podemos enviar información desde un formulario a través de diferentes métodos, GET o POST. Para acceder a esta información desde PHP llamaremos a $_GET y $_POST, estas son variables super globales.
<?php
//Muestra los datos enviados si el método del formularios es GET
var_dump($_GET);
//Muestra los datos enviados si el método del formularios es POST
var_dump($_POST);
?>
<form action="index.php" method="post">
<input type="text" name="title">
<input type="text" name="description">
<br/>
<button type="submit">Save</button>
</form>
Packagist es un sitio donde encontraras múltiples librerías de terceros que puedes integrar a tus proyectos mediante composer, de aquí añadiremos nuestra herramienta para la conexión a base de datos.
Realizamos la conexión con la base de datos y usaremos Eloquent como nuestro ORM. Para que funcionen los modelos debemos hacer una clase que herede de Model.
Para instalar Eloquent se va a usar el siguiente comando:
$ php composer.phar require illuminate/database
Este comando agrega illuminate/database a la lista de require del composer.json.
Asimismo, se crea un arechivo composer.lock en donde se almacenan todas las dependencias de illuminate/database.
Una vez estén listos los modelos, usando la variable super global $_POST conectaremos la información del formulario con nuestro modelo para añadirla información a la base de datos.
Para configurar la conexión a la base de datos, se va a hacer de la siguiente forma:
use Illuminate\Database\Capsule\Manager as Capsule;
$capsule = new Capsule;
$capsule->addConnection([
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'cursophp',
'username' => 'root',
'password' => 'root',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
]);
$capsule->setAsGlobal();
$capsule->bootEloquent();
Para conectar una clase con una tabla de base de datos, se debe de hacer 2 cosas:
- Un use a Illuminate\Database\Eloquent\Model.
- Setear la variable $table con el nombre de la tabla de base de datos.
use Illuminate\Database\Eloquent\Model;
class Job extends Model {
protected $table = 'jobs';
}
El siguiente es un ejemplo de cómo agregar datos en una base de datos.
$job = new Job();
$job->title = $_POST['title'];
$job->description = $_POST['description'];
$job->save();
$jobs = Job::all();
Front Controller es un patrón de diseño que nos dará una solución a la redundancia del código en las múltiples entradas a la aplicación. Este tendrá el control de todas las entradas además de las inicializaciones de base de datos etc.
El front controller se pondrá en el archivo public/index.php.
En este archivo se pondrán todas las funcionalidades que necesitan todos los archivos de php:
- Control de errores
- Autoloader
- Acceso a la base de datos
Al final del front controller, se va a hacer un require de la página a mostrar en base el router.
$route = $_GET['route'] ?? '/';
if($route == '/')
require('../index.php');
elseif ($route == 'addJob')
require('../addJob.php');
PSR7 es un estandar que nos permite normalizar un request y un response en PHP.
En este enlace encontrarás la documentación de PSR7: https://www.php-fig.org/psr/psr-7/
Aquí encontrarás cómo implementarlo: https://zendframework.github.io/zend-diactoros/usage/
Los request se van a manejar con una librería llamada diactoros.
$ php composer.phar require zendframework/zend-diactoros
Luego de esto, se va a agregar la siguiente línea de código:
$request = Zend\Diactoros\ServerRequestFactory::fromGlobals(
$_SERVER,
$_GET,
$_POST,
$_COOKIE,
$_FILES
);
Luego de esto, para manejar los request, se va a crear un archivo .htaccess en la raiz del proyecto.
RewriteEngine On
# Evitar ciclos
RewriteCond %{THE_REQUEST} /platziphp/public/([^\s?]*) [NC]
RewriteRule ^ %1 [L,NE,R=302]
RewriteRule ^((?!platziphp/public/).*)$ platziphp/public/$1 [L,NC]
Asimismo, se va a escribir otro archivo .htaccess en la carpeta public.
RewriteEngine On
#Verificar si es directorio
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
aura/router es un paquete que nos ayudará para manejar las rutas en nuestro proyecto.
Para instalarlo solo hay que escribir el siguiente comando en la terminal:
$ php composer.phar require aura/router
Luego de eso escribir las siguientes líneas de código:
$routerContainer = new RouterContainer();
//Setear las rutas
$map = $routerContainer->getMap();
$map->get('index', '/', '../index.php');
$map->get('addJobs', '/jobs/add', '../addJob.php');
$matcher = $routerContainer->getMatcher();
$route = $matcher->match($request);
if (!$route) {
echo 'No route';
} else {
require $route->handler;
}
MVC es un patrón de diseño que divide nuestra aplicación en tres partes fundamentales:
- Model
- View
- Controller
Un proyecto en MVC se organiza en carpetas con estos nombres.
En las vistas se encuentra el código HTML. La vista debe de encontrarse sin nungún tipo de lógica de negocio.
Los controladores unen el modelo con las vistas.
use App\Models\Job;
class IndexController {
public function indexAction() {
$jobs = Job::all();
include '../views/index.php';
}
}
En MVC, se va a cambiar un poco la forma en la cual se usan las rutas. Ahora las rutas, en vez de redirigir a un archivo, van a redirigir a una acción dentro de un controlador.
$routerContainer = new RouterContainer();
$map = $routerContainer->getMap();
$map->get('index', '/', [
'controller' => 'App\Controllers\IndexController',
'action' => 'indexAction'
]);
$map->post('saveJobs', '/jobs/add', [
'controller' => 'App\Controllers\JobsController',
'action' => 'getAddJobAction'
]);
$matcher = $routerContainer->getMatcher();
$route = $matcher->match($request);
if (!$route) {
echo 'No route';
} else {
$handlerData = $route->handler;
$controllerName = $handlerData['controller'];
$actionName = $handlerData['action'];
$controller = new $controllerName;
$controller->$actionName($request);
}
class JobsController {
public function getAddJobAction($request) {
if(!empty($request->getMethod() == 'POST')) {
$postData = $request->getParsedBody();
$job = new Job();
$job->title = $postData['title'];
$job->description = $postData['description'];
$job->save();
}
include('../views/addJob.php');
}
}
Son motores que sirven para renderear el código html y sustituir las partes de código con los datos que se tienen que imprimir.
PHP, a pesar de que fue pensado para ser un template engine, se ha enfocado más en la programación, por eso tenemos librerías que se concentran totalmente en esto.
Twig es un Template Engine que nos ayudará a manejar la seguridad en los elementos de entrada de la aplicación.
Para instalar Twig, se hace de la siguiente forma:
$ php composer.phar require twig/twig
Para poder cargar los templates, se va a crear un controlador base:
namespace App\Controllers;
class BaseController {
protected $templateEngine;
public function __construct() {
//declarar la carpeta en donde se encuentran los templates
$loader = new \Twig_Loader_Filesystem('../views');
$this->templateEngine = new \Twig_Environment($loader, array(
'debug' => true,
'cache' => false,
));
}
public function renderHTML($fileName, $data = []) {
return new HtmlResponse($this->templateEngine->render($fileName, $data));
}
}
Luego, para cargar una plantilla, se hace de la siguiente forma:
class IndexController extends BaseController{
public function indexAction() {
return $this->renderHTML('index.twig');
}
}
Además, cabe resaltar que la plantilla a cargar debe de tener la extensión .twig.
Twig no permite código PHP en sus plantillas. Para agregar data dinamica, se va a usar {{ }}
.
<h1>{{ name }}</h1>
Luego, el valor de las variables se pasan desde el controlador:
class IndexController extends BaseController {
public function indexAction() {
$jobs = Job::all();
return $this->renderHTML('index.twig', [
'name' => 'Sergio'
]);
}
}
Se va a usar {% for item in items %}{% endfor %}
para declarar cuándp una parte del código HTML se va a reperir.
{% for job in jobs %}
<h5>{{ job.title }}</h5>
{% endfor %}
Al declarar un layout, se va a usar {% block content %}{% endblock %}
para indicar en qué parte del layout se va a mostrar el contenido de los HTML hijos.
<!doctype html>
<html lang="en">
<head>
<title>Resume</title>
</head>
<body>
<div class="container">
{% block content %}
{% endblock %}
</div>
</body>
</html>
Para usar el layout, se tiene que hacer lo siguiente:
- Declarar el layout con usando
extends
. - Indicar el código HTML sue se va a mostrar con
block content
.
{% extends "layout.twig" %}
{% block content %}
<p>Estoy usando un layout</p>
{% endblock %}
Para poder subir archivo, se debe de agregar enctype="multipart/form-data"
en el formulario.
<form action="/jobs/add" method="post" enctype="multipart/form-data">
<input type="file" name="logo">
<button type="submit">Save</button>
</form>
Luego, en el controlador, agregar las siguientes líneas de código:
public function uploadFile($request) {
//Obtiene los archivos que se han subido
$files = $request->getUploadedFiles();
$logo = $files['logo'];
//Verifica que no hayan errores
if($logo->getError() == UPLOAD_ERR_OK) {
//Obtiene el nombre del archivo
$fileName = $logo->getClientFilename();
//Mueve el archivo a la carpeta seleccionada
$logo->moveTo("uploads/$fileName");
}
}
Cuando permites subir archivos a tu servidor, estas exponiendote a posibles ataques o hackeos, por eso es muy importante tratar de tener cuidado con la forma en la que almacenamos los archivos.
Existen muchas formas de protegernos, pero aun asi, aqui te dejo algunos tips para que tengas mas tranquilidad a la hora de permitir que usuarios suban archivos a tus aplicaciones:
De ser posible, almacena los archivos en un servicio externo a tu aplicación como Amazon S3
Un ataque muy común es tratar de subir un archivo que no es lo que nosotros esperamos, un atacante podría intentar subir un archivo PHP para que al mandarlo llamar el tenga acceso al servidor, pero si almacenamos los archivos en otro sitio él no tendrá acceso a nuestro servidor.
Cuida los permisos
Cuando subimos un archivo podemos usar la función chmod para cambiar los permisos e indicarle al sistema operativo que ese archivo no es ejecutable.
Verifica el tipo de archivo que están subiendo
Podemos verificar la extensión del archivo, el mime type y lo encabezados para validar que el archivo es del tipo que estamos pidiendo.
Limita el tamaño de los archivos permitidos
Dependiendo de tu aplicación, tu puedes saber qué tamaño de archivo es razonable, de ser así, limita el tamaño de la subida de archivos.
Si tu aplicación usa login
Solo permite que los usuarios registrados hagan la subida de archivos.
Genera un nombre de archivo aleatorio y añade la extensión previamente generada
Muchas veces los atacantes trataran de esconder sus archivos, tu puedes cambiar el nombre de un archivo y poner otro que para ti sea claro que es un archivo subido por un usuario.
No confíes sólo en una validación por el lado del cliente, no es suficiente
Lo ideal es implementar una seguridad tanto por el lado del cliente como por el lado del servidor. También podemos comenzar validaciones del lado del cliente usando javascript, sin duda son buenas, pero es relativamente sencillo sobrepasarse y enviar un archivo malicioso.
No uses solo una medida de protección
Combina todas las que te sean posibles para tener mayor seguridad en tu app.
Las validaciones son importantes para proteger la integridad de la información que vamos a almacenar y mostrar.
Una de las librerías que permiten el manejo de validaciones es respect/validation.
$ php composer.phar require respect/validation
use Respect\Validation\Validator as v;
class JobsController extends BaseController{
public function getAddJobAction($request) {
$responseMessage = null;
if(!empty($request->getMethod() == 'POST')) {
//Setear reglas de validación
$jobValidator = v::key('title', v::stringType()->notEmpty())
->key('description', v::stringType()->notEmpty());
try {
//Obtener la dada del request
$postData = $request->getParsedBody();
//Validar los datos
$jobValidator->assert($postData);
$responseMessage = 'Validado';
}
catch(\Exception $e) {
$responseMessage = $e->getMessage();
}
}
return $this->renderHTML('addJob.twig', [
'responseMessage' => $responseMessage
]);
}
}
Las validaciones de password las haremos con la función password_verify()
.
//Verificar si el usuarios existe
$user = User::where('email', $postData['email'])->first();
if($user) {
//Verificar si coincide el usuari y la contrasela
if(\password_verify($postData['password'], $user->password)) {
echo 'user authenticated';
}
else
echo 'wrong password';
}
else {
echo 'Not found';
}
Para redrigir hay que agregar un use a Zend\Diactoros\Response\RedirectResponse
.
use Zend\Diactoros\Response\RedirectResponse;
Luego, la redirección se hace de la siguiente manera:
return new RedirectResponse('/admin');
Además, para que las redirecciones funcionen, hay que agregar las cabeceras al response.
foreach($response->getHeaders() as $name => $values) {
foreach($values as $value) {
header(sprintf('%s: %s', $name, $value), false);
}
}
http_response_code($response->getStatusCode());
echo $response->getBody();
Para poder usar las sesiones, se debe de usar la sentencia session_start
.
session_start();
Para setear un valor en la sesión:
$_SESSION['userId'] = $user->id;
Para leer el valor de una sesión:
$userId = $_SESSION['userId'];
Para restringir el acceso a ciertas rutas, se va a agregar un key al array de enrutamiento:
$map->get('admin', '/admin', [
'controller' => 'IndexController',
'action' => 'getIndex',
'auth' => true
]);
Luego, al momento de mostrar la respectiva plantilla para el ruteo, hacer hacer una verificación de la sesión en casi se requiera autenticación:
$needsAuth = $handlerData['auth'] ?? false;
$sessionUserId = $_SESSION['userId'] ?? null;
if($needsAuth && !$sessionUserId) {
echo 'access denied';
die;
}
Los datos de configuración de la base de datos no deberían de encontrarse hardcodeados. Es una buena práctica usar variables de entorno para sacarlas de ahí.
Utilizaremos vlucas/phpdotenv para cargar las variables de entono en super variables.
Se va a instalar de la siguiente manera:
$ php composer.phar require vlucas/phpdotenv
Para usarlo, se debe de crear un archivo .env en donde se van a almacenar las variables de entorno:
BD_HOST=localhost
DB_DATABASE=cursophp
Para obtener los datos de las variables, se usa getenv
:
$host = getenv('DB_HOST');
$database = getenv('DB_DATABASE');
Para poder desplegar en Heroku se tiene que hacer lo siguiente:
-
Crear una nueva app desde el hashboard de Heroku.
-
Linkear al repositorio remoto de Heroku a la app.
-
Hacer un push de la aplicación al repositorio remoto de Heroku.
$ git push heroku master
-
Instalar Heroku Posgress: https://elements.heroku.com/addons/heroku-postgresql
-
Luego, te vas a la sección de resources de tu app en Heroku y entras a Heroku Postgres :: Database
-
Te vas a la pestaña Settings y ahí te deberían de aparecer los datos de conexión a la DB de Posgress
-
Vuelves a la vista de tu app en Heroku y vas a la pestaña Settings.
-
Haces click en Reveal Config Vars.
-
Agregas las variables de entorno con los datos de conexión a la base de datos. Estas variables son las mismas que has agrega en el archivo .env solo que contienen la data de conexión a la base de datos de postgress.
BD_HOST
DB_NAME
DB_USER
DB_PASS
DB_DRIVER
DB_PORT