Logo de PHP

PHP

Es un lenguaje de programación interpretado, de propósito general de alto nivel, que se puede utilizar para desarrollar aplicaciones web, aplicaciones de escritorio, aplicaciones móviles, etc. Es open source y multiplataforma. Puede o no ser orientado a objetos. Tiene funciones para conectarse a bases de datos ya definidas, sin importar librerias.

php -v para ver la versión de PHP instalada.

php -S localhost:3000 para lanzar el servidor de PHP en el puerto 3000. Se lanza desde la raiz del proyecto que me interesa.

Herramientas

AMP

AMP Stack: Apache, MySQL, PHP.

Los archivos PHP que se van a levantar con el servidor de apache deben estar en la carpeta C:/apache/htdocs.

Reiniciar el servicio cuando se hacen cambios en los archivos de configuración.

Composer

Es un gestor de dependencias para PHP. Es equivalente a NPM en JavaScript.

Intervention Image

Es una libreria para manipular imágenes.
composer require intervention/image instalar la libreria.

Packagist

Es un repositorio de paquetes de PHP. Para buscar paquetes de PHP que necesitemos.

Conceptos

Para escribir código PHP se debe usar la etiqueta <?php ?>. En scripts que solo tengan código PHP se puede omitir la etiqueta de cierre ?>, porque existe una vulnerabilidad que permitiria inyectar código malicioso en el código PHP.
Cada instrucción debe terminar con ;.
Los comentarios se hacen con // o /* */.

Superglobales

Son variables que están disponibles en todo el script.

<?php
  // $_GET
  echo $_GET['name'];
  // $_POST
  echo $_POST['name'];
  // $_SERVER
  echo $_SERVER['SERVER_NAME'];
  // $_SESSION
  session_start();
  $_SESSION['name'] = 'Juan';
  echo $_SESSION['name'];
  // $_COOKIE
  echo $_COOKIE['font-size'];
  // $_FILES
  echo $_FILES['file']['name'];
  // $_ENV
  echo $_ENV['USER'];
  // $_REQUEST
  echo $_REQUEST['name'];
  // $_GLOBALS
  echo $GLOBALS['name'];
?>

Variables

Se definen con el signo $ y el nombre de la variable.
No se puede empezar con un número, se usa camelCase o snake_case.
Se puede reasignar el valor de una variable.

Para definir una constante o variable que no se puede reasignar se usa define('NAME', 'value');. Tambien se puede usar const NAME = 'value'; a partir de PHP 5.3 pero no es tan común. Es buena práctica usar mayúsculas y snake_case para el nombre de las constantes.

<?php
  // string
  $name = 'Juan';
  $name = 'Pablo';
  define('NAME', 'Juan');
  const NAME = 'Juan';
  // int
  $age = 25;
  // float
  $height = 1.85;
  // boolean
  $isMale = true;
  // null
  $salary = null;
  // array
  $people = ['Juan', 'Pablo', 'Pedro'];

  echo $name;
  var_dump($name);
  echo NAME;
?>

Tipos de datos

PHP es un lenguaje de tipado dinámico, no se especifica el tipo de dato de la variable. PHP se encarga de asignar el tipo de dato a la variable.

String

Se puede usar comilla doble o simple para definir un string.

<?php
  $name = 'Juan';
  $name = "Juan";
?>

Se puede concatenar con . o interpolación con o sin {}. Si se quiere usar interpolación se debe usar comilla doble. Es más común usar el punto porque en ciertos casos la interpolación puede llevar a una vulnerabilidad.

<?php
  $name = 'Juan';
  echo 'Hola ' . $name;
  echo "Hola $name";
  echo "Hola {$name}";
?>

Números

Se pueden usar números enteros o decimales.

<?php
  $age = 25;
  $height = 1.85;
?>

Boolean

Los valores true se representan con 1 y false y null se representan como vacio, no devuelven ningun valor.

<?php
  $isMale = true;
?>

Null

Null en mayúscula o minúscula es lo mismo.
is_null($variable) para saber si una variable es null.

<?php
  $salary = null;
  $isActive = is_null($salary);
?>

Array

Existen 2 tipos de arreglos, los indexados y los asociativos. Los indexados son los que tienen solo valor. Los asociativos son los que tienen clave y valor y son equivalentes a los objetos de JavaScript.
Los arrays pueden contener cualquier tipo de dato. Los arrays pueden tener claves alfanuméricas.

<?php
  // indexados
  $people = ['Juan', 'Pablo', 'Pedro'];
  $people = array('Juan', 'Pablo', 'Pedro');
  $people = array('Juan', 25, true);
  // asociativos
  $people = [
    'name' => 'Juan',
    'age' => 25,
    'informacion' => [
        'tipo' => 'premium',
        'disponible' => 100
    ]
  ];
  // Util para ver el contenido de un array
  echo "<pre>";
  var_dump($people);
  echo "</pre>";
?>

Fechas

Operadores de incremento y decremento

<?php
  $age = 25;
  echo $age++; // 25
  echo $age; // 26
  echo ++$age; // 27
?>

Operadores de comparación

<?php
  $age = 25;
  $age2 = '25';
  var_dump($age == $age2); // true
  var_dump($age === $age2); // false
  var_dump($age != $age2); // false
  var_dump($age !== $age2); // true
  var_dump($age <=> $age2); // 0
  var_dump($age ?? $age2); // 25
?>

Operadores lógicos

<?php
  $age = 25;
  $name = 'Juan';
  if ($age >= 18 && $name === 'Juan') {
    echo 'Eres mayor de edad';
  }
?>

Condicionales

Si se usa la sintaxis de : y endif se debe usar elseif en lugar de else if.

<?php
  $age = 25;
  // if elseif
  if ($age >= 18) {
    echo 'Eres mayor de edad';
  } elseif ($age >= 16) {
    echo 'Eres adolescente';
  } else {
    echo 'Eres menor de edad';
  }
  // if elseif con otra sintaxis
  if ($age >= 18):
    echo 'Eres mayor de edad';
  elseif ($age >= 16):
    echo 'Eres adolescente';
  else:
    echo 'Eres menor de edad';
  endif;
  // switch
  switch ($age) {
    case 18:
      echo 'Eres mayor de edad';
      break;
    case 16:
      echo 'Eres adolescente';
      break;
    default:
      echo 'Eres menor de edad';
      break;
  }
  // operador ternario
  echo ($age >= 18) ? 'Eres mayor de edad' : 'Eres menor de edad';
?>

Ciclos

<?php
  // for
  for ($i = 0; $i < 10; $i++) {
    echo $i;
  }
  // for con otra sintaxis
  for ($i = 0; $i < 10; $i++):
    echo $i;
  endfor;
  // while
  $i = 0;
  while ($i < 10) {
    echo $i;
    $i++;
  }
  // do while
  $i = 0;
  do {
    echo $i;
    $i++;
  } while ($i < 10);
  // foreach indexado
  $people = ['Juan', 'Pablo', 'Pedro'];
  foreach ($people as $person) {
    echo $person;
  }
  // foreach con otra sintaxis
  $people = ['Juan', 'Pablo', 'Pedro'];
  foreach ($people as $person):
    echo $person;
  endforeach;
  // foreach asociativo
  $person = [
    'name' => 'Juan',
    'age' => 25,
    'informacion' => [
        'tipo' => 'premium',
        'disponible' => 100
    ]
  ];
  foreach ($person as $value) {
    echo $value;
  }
  foreach ($person as $key => $value) {
    echo $key . ': ' . $value;
  }
?>

Ejemplo de foreach con PHP y HTML:

<?php
  $productos = [
    [
      'nombre' => 'Tablet',
      'precio' => 200,
      'disponible' => true
    ],
    [
      'nombre' => 'Television 24"',
      'precio' => 300,
      'disponible' => true
    ],
    [
      'nombre' => 'Monitor Curvo',
      'precio' => 400,
      'disponible' => false
    ]
  ];

  foreach( $productos as $producto) { ?>
    <li>
      <p>Producto: <?php echo $producto['nombre']; ?> </p>
      <p>Precio: <?php echo "$" . $producto['precio']; ?> </p>
      <p><?php echo ($producto['disponible']) ? 'Disponible' : 'No Disponible';  ?> </p>
    </li>
    <?php
  }
?>

Funciones

Las funciones no pueden acceder a las variables definidas por fuera de su scoope. Tendriamos que pasarle la variable como parametro a la función.

<?php
  // con parámetros por defecto
  function saludar($name = 'Juan', $age = 25) {
    return "Hola " . $name . " tienes " . $age . " años";
  }
  echo saludar();
  echo saludar('Pablo', 30);

  // con parametros y tipado
  function sumar(int $num1, int $num2): int {
    return $num1 + $num2;
  }
  echo sumar(1, 2);

  // parametros nombrados para PHP 8
  function sumar(int $num1, int $num2): int {
    echo $num1;
    return $num1 + $num2;
  }
  echo sumar(num2: 1, num1: 2);

  // ? indica que la función puede retornar o no un string
  function isAuthorized(bool $isAuth): ?string {
    return $isAuth ? 'Acceso permitido' : null;
  }
  echo isAuthorized(false);

  // puede haber union de tipos
  function isAuthorized(bool $isAuth): string|int {
    return $isAuth ? 'Acceso permitido' : 0;
  }
  echo isAuthorized(false);

  // con parámetros por defecto usando global ???
  $name = 'Juan';
  function saludar() {
    global $name;
    echo "Hola " . $name;
  }
  saludar();
?>

Clases

Es una plantilla para crear objetos. Tienen propiedades y métodos. Se define con PascalCase. Las caracteristicas más importantes de la programación orientada a objetos son:

Abstracción: Es la capacidad de representar un objeto del mundo real en un programa.
Encapsulamiento: Es la capacidad de agrupar datos y métodos en una clase.
Herencia: Es la capacidad de una clase de heredar propiedades y métodos de otra.
Polimorfismo: Es la capacidad de una clase de tener diferentes comportamientos.

<?php
  class Product {
    protected $name;
    public $price;
    public $available;
    public static $image = "image.jpg";

    public function __construct(string $name, int $price, bool $available) {
      $this->name = $name;
      $this->price = $price;
      $this->available = $available;
    }

    public static function getProductImage() {
        return self::$image;
    }

    public function getName(): string {
      return $this->name;
    }

    public function setName(string $name) {
      $this->name = $name;
    }

    public function showInfo() {
      return "El producto es: " . $this->name . " y su precio es: " . $this->price;
    }
  }

  // otra forma de definir una clase. Desde PHP 8
  class Product {
    public function __construct(
      protected string $name,
      public int $price,
      public bool $available
    ) {}
  }

  $product = new Product('Tablet', 200, true);
  $product->setName('Iphone');
  echo $product->getName();
  echo $product->showInfo();

  // acceder a una propiedad o metodo estáticos
  echo Product::$image;
  echo Product::getProductImage();
?>

Herencia

Permite crear nuevas clases a partir de otras clases existentes.

<?php
  class Product {
    protected $name;
    public $price;
    public $available;
    public static $image = "image.jpg";

    public function __construct(string $name, int $price, bool $available) {
      $this->name = $name;
      $this->price = $price;
      $this->available = $available;
    }

    public static function getProductImage() {
        return self::$image;
    }

    public function getName(): string {
      return $this->name;
    }

    public function setName(string $name) {
      $this->name = $name;
    }

    public function showInfo() {
      return "El producto es: " . $this->name . " y su precio es: " . $this->price;
    }
  }

  class ProductDigital extends Product {
    public $downloadable;

    public function __construct(
      string $name,
      int $price,
      bool $available,
      bool $downloadable
    ) {
      parent::__construct($name, $price, $available);
      $this->downloadable = $downloadable;
    }

    public function showInfo() {
      return parent::showInfo() . " y es descargable";
    }
  }

  $product = new ProductDigital('Tablet', 200, true, true);
  echo $product->showInfo();
?>

Clases abstractas

Sirven para definir una clase base que se va a heredar pero no se puede instanciar. Se usa para definir métodos que se deben implementar en las clases hijas.

<?php
  abstract class Product {
    protected $name;
    public $price;
    public $available;
    public static $image = "image.jpg";

    public function __construct(string $name, int $price, bool $available) {
      $this->name = $name;
      $this->price = $price;
      $this->available = $available;
    }

    public static function getProductImage() {
        return self::$image;
    }

    public function getName(): string {
      return $this->name;
    }

    public function setName(string $name) {
      $this->name = $name;
    }

    abstract public function showInfo();
  }

  class ProductDigital extends Product {
    public $downloadable;

    public function __construct(
      string $name,
      int $price,
      bool $available,
      bool $downloadable
    ) {
      parent::__construct($name, $price, $available);
      $this->downloadable = $downloadable;
    }

    public function showInfo() {
      return "El producto es: " . $this->name . " y su precio es: " . $this->price;
    }
  }

  $product = new ProductDigital('Tablet', 200, true, true);
  echo $product->showInfo();
?>

Polimorfismo

Es la capacidad de una clase de tener diferentes comportamientos.

<?php
  abstract class Product {
    protected $name;
    public $price;
    public $available;

    public function __construct(string $name, int $price, bool $available) {
      $this->name = $name;
      $this->price = $price;
      $this->available = $available;
    }

    public function getName(): string {
      return $this->name;
    }

    public function setName(string $name) {
      $this->name = $name;
    }

    abstract public function showInfo();
  }

  class ProductDigital extends Product {
    public $downloadable;

    public function __construct(
      string $name,
      int $price,
      bool $available,
      bool $downloadable
    ) {
      parent::__construct($name, $price, $available);
      $this->downloadable = $downloadable;
    }

    public function showInfo() {
      return "El producto es: " . $this->name . " y su precio es: " . $this->price . "y es descargable";
    }
  }

  class ProductPhysical extends Product {
    public $weight;

    public function __construct(
      string $name,
      int $price,
      bool $available,
      int $weight
    ) {
      parent::__construct($name, $price, $available);
      $this->weight = $weight;
    }

    public function showInfo() {
      return "El producto es: " . $this->name . " y su precio es: " . $this->price . "y pesa: " . $this->weight . "kg";
    }
  }

  $product = new ProductDigital('Tablet', 200, true, true);
  echo $product->showInfo();
  $product = new ProductPhysical('Tablet', 200, true, 2);
  echo $product->showInfo();
?>

Interfaces

Sirven para definir un contrato que se debe cumplir en las clases que la implementan.

<?php
  interface Product {
    public function showInfo(): string;
  }

  class ProductDigital implements Product {
    public $name;
    public $price;

    public function __construct(string $name, int $price) {
      $this->name = $name;
      $this->price = $price;
    }

    public function showInfo(): string {
      return "El producto es: " . $this->name . " y su precio es: " . $this->price;
    }
  }

  $product = new ProductDigital('Tablet', 200, true, true);
  echo $product->showInfo();
?>

Sanitizar y validar datos

<?php
  $email = "correo@correo.com/";
  // sanitizar
  $resultado = filter_var($email, FILTER_SANITIZE_EMAIL);
  var_dump($resultado); // string(17)"correo@correo.com"

  // validar
  $resultado = filter_var($email, FILTER_VALIDATE_EMAIL);
  var_dump($resultado); // bool(false)

  // escapar/sanitizar el html
  $resultado = "<script>alert('hola')</script>";
  $resultado = htmlspecialchars($resultado);
  // $resultado = &lt;script&gt;alert('hola')&lt;/script&gt;
  // escapa los caracteres especiales y evita inyeccion de codigo
?>

Formularios

Autenticación

Sesiones

<?php
  // iniciar una sesión
  session_start();

  // acceder a una sesión
  $_SESSION['usuario'] = 'Juan';
  $_SESSION['login'] = true;

  // destruir una sesión
  session_destroy();
?>

Cookies

Se puede usar la función time() para obtener el tiempo actual y sumarle el tiempo de expiración de la cookie.
Para eliminar una cookie se puede poner un tiempo de expiración en el pasado. Por ejemplo: time() - 1. Para que la cookie esté disponible en todo el sitio se puede poner la ruta como '/'.
Es recomendable sanitizar las cookies para evitar inyección de código. Ej:
htmlspecialchars($_COOKIE['font-size'])

<?php
  // crear una cookie
  setcookie('font-size', '30px', time() + 60 * 60 * 24 * 30, '/');

  // acceder a una cookie
  echo $_COOKIE['font-size'];
?>

Archivos

<?php
  // obtener el contenido de un archivo
  $file = file_get_contents('archivo.txt');
  echo $file;

  // escribir en un archivo
  file_put_contents('archivo.txt', 'Hola Mundo', FILE_APPEND);

  // obtener el contenido de un archivo como un array
  $file = file('archivo.txt');
  echo $file[0];

  // eliminar un archivo
  unlink('archivo.txt');

  // copiar un archivo
  copy('archivo.txt', 'archivo2.txt');

  // renombrar un archivo
  rename('archivo.txt', 'archivo2.txt');

  // crear una carpeta
  mkdir('carpeta');

  // eliminar una carpeta
  rmdir('carpeta');

  // saber si es una carpeta
  is_dir('carpeta');

  // obtener el contenido de una carpeta como un array
  $files = scandir('./');
  echo "<pre>";
  var_dump($files);
  echo "</pre>";

  // obtener los archivos de una carpeta como un array.
  $resultados = glob('*.php');
  $resultados = glob('*.{php,html,txt}', GLOB_BRACE);
?>

Namespaces

Sirven para evitar conflictos entre clases que tienen el mismo nombre.

<?php
  // Tienda.php
  namespace Tienda;

  class Producto {
    public function mostrar() {
      echo "Mostrar Tienda";
    }
  }

  // Tienda2.php
  namespace Tienda2;

  class Producto {
    public function mostrar() {
      echo "Mostrar Tienda2";
    }
  }

  // index.php
  // importar el autoloader de composer
  require_once 'vendor/autoload.php';

  // importar un namespace
  use Tienda\Producto as ProductoTienda;
  use Tienda2\Producto as ProductoTienda2;

  $producto = new ProductoTienda();
  $producto->mostrar();
  $producto = new ProductoTienda2();
  $producto->mostrar();
?>

Agregar en el composer.json para que se pueda hacer el autoload de los namespaces:

{
  "autoload": {
    "psr-4": {
      "Tienda\\": "Tienda",
      "Tienda2\\": "Tienda2"
    }
  }
}

json_encode y json_decode

<?php
  $productos = [
    [
        'nombre' => 'Tablet',
        'precio' => 200,
        'disponible' => true
    ],
    [
        'nombre' => 'Televisión 24"',
        'precio' => 300,
        'disponible' => true
    ],
    [
        'nombre' => 'Monitor Curvo',
        'precio' => 400,
        'disponible' => false
    ]
  ];

  echo "<pre>";
  var_dump($productos);

  // convertir un array a un string JSON.
  $productos_json = json_encode($productos, JSON_UNESCAPED_UNICODE);

  // para convertir un string JSON a un array.
  $productos_array = json_decode($productos_json);

  var_dump($productos_json);
  var_dump($productos_array);
  var_dump($productos_array[0]->nombre);
  echo "</pre>";
?>

Conexión a bases de datos

Existen 2 formas de conectarse a una base de datos, usando la libreria MySQLi o usando PDO. Ambas vienen incluidas en PHP (Quizá se tenga que activar algo en la configuración de PHP para usarlas).

PDO permite conectarse a cualquier base de datos que tenga un driver PDO (MySQL, Oracle, PostgreSQL, SQLite, etc). Solo se puede conectar con la forma orientada a objetos.

MySQLi solo permite conectarse a bases de datos MySQL.
Todas las funciones de MySQLi inician con mysqli_. Anteriormente se usaba el prefijo mysql_ pero ya es obsoleto. Se puede conectar con la forma orientada a objetos o con función.

El método query se podria utulizar cuando yo como programador creo la consulta y no hago interpolación de los datos. Si se requiere un dato que el usuario ingresa para armar la consulta, se debe usar sentencias preparadas.

Tambien se puede usar la forma orientada a objetos para conectarse a la base de datos.

Tambien se puede hacer con sentencias preparadas. Se usa para evitar inyección SQL. Una vez preparada la sentencia, se guarda en memoria, en caso de hacer la misma consulta.

<?php
  // obtener datos de la url
  $id = $_GET['id'];
  $nombre = $_GET['nombre'];

  // Conectar a la BD con Mysqli.
  // mysqli('host', 'user', 'password', 'database');
  $db = new mysqli('localhost', 'root', '', 'bienesraices_crud');

  // Creamos el query
  $query = "SELECT titulo, imagen from propiedades";

  // Lo preparamos
  $stmt = $db->prepare($query);

  // Lo ejecutamos
  $stmt->execute();

  // creamos las variables donde se guardaran los resultados
  // bind_result() nos permite pasar los resultados a variables
  $stmt->bind_result($titulo, $imagen);

  // imprimir el resultado
  // fetch() trae un resultado de la consulta
  while($stmt->fetch()):
      var_dump($titulo);
  endwhile;

  // si se requiere inyectar datos en la consulta
  // ? indica que se va a inyectar un dato
  // bind_param() nos permite inyectar datos en la consulta
  // se debe poner una letra por cada dato que se va a inyectar
  // s indica que el dato es un string
  // i indica que el dato es un entero
  // d indica que el dato es un double
  $query = "SELECT titulo, imagen from propiedades WHERE id = ? OR nombre = ?";
  $stmt = $db->prepare($query);
  $stmt->bind_param('is', $id, $nombre);
  $stmt->execute();
  $stmt->bind_result($titulo, $imagen);
  while($stmt->fetch()):
      var_dump($titulo);
  endwhile;
?>

Con PDO usando sentencias preparadas:

<?php
  // obtener datos de la url
  $id = $_GET['id'];
  $nombre = $_GET['nombre'];

  // Conectar a la BD con PDO
  // PDO('database:host=host; dbname=database', 'user', 'password');
  $db = new PDO('mysql:host=localhost; dbname=bienesraices_crud', 'root', '');

  // Creamos el query
  $query = "SELECT titulo, imagen from propiedades";

  // Lo preparamos
  $stmt = $db->prepare($query);

  // Lo ejecutamos
  $stmt->execute();

  // Obtener los resultados
  // fetchAll(como traer el resultado) nos permite obtener todos los resultados
  $resultado = $stmt->fetchAll( PDO::FETCH_ASSOC );

  // Iterar
  foreach($resultado as $propiedad):
      echo $propiedad['titulo'];
      echo "<br>";
      echo $propiedad['imagen'];
      echo "<br>";
  endforeach;

  // si se requiere inyectar datos en la consulta ???
  $query = "SELECT titulo, imagen from propiedades WHERE id = :id OR nombre = :nombre";
  $stmt = $db->prepare($query);
  // bindValue() nos permite inyectar datos en la consulta
  $stmt->bindValue(':id', $id);
  $stmt->bindValue(':nombre', $nombre);
  $stmt->execute();
  // o se podria hacer asi:
  // $stmt->execute(
  //   array(
  //     ':id' => $id,
  //     ':nombre' => $nombre
  //   )
  // );
  $resultado = $stmt->fetchAll( PDO::FETCH_ASSOC );
  foreach($resultado as $propiedad):
      echo $propiedad['titulo'];
      echo "<br>";
      echo $propiedad['imagen'];
      echo "<br>";
  endforeach;
?>

MVC

Es un patrón de diseño de software que separa la lógica de negocio de la interfaz de usuario. Se divide en 3 capas: