
Programar con WordPress
equivale a programar con php
, y aunque utilices entornos de desarrollo que te faciliten la vida, al final necesitarás saber php
Pues aquí damos un paseo rápido al lenguaje
Prólogo
PHP o Hypertext Preprocessor es un lenguaje diseñado exclusivamente para desarrollo web pensado para insertarse en código HTML
Como lenguaje, y en mi opinión viniendo de JavaScript
, es un lenguaje pesado y poco amable con el que escribir y al que le falta muchas de las ayudas y características del JavaScript más moderno
Pero sin duda es un lenguaje potente y en algunos casos con funciones más versátiles que las que tenemos en JS
De cualquier manera, WordPress es php
(aunque ahora también sea React
)
Vamos allá
Definir nuestro código php
Lo primero, aunque nuestro archivo se llame hola.php
y ya con la extension .php
está claro que es un archivo php
, necesitamos igualmente informar al procesador cuando estamos escribiendo php
Esto lo hacemos con simples tags que se abren y se cierran
<?php
// nuestro código
?>
Todo lo que esté fuera de estos tags no es código php
, y esto es lo que nos permitirá insertar php
dentro de html
y viceversa (lo que vendría a ser el equivalente al JSX
de React
)
Un ejemplo sería esto
<p>Hola cómo estamos</p>
<?php if ($auth_loged == true): ?>
<p>Veo que estás logeado!</p>
<?php else: ?>
<p>Veo que estás de visita</p>
<?php endif; ?>
Para mejorar lo anterior (que es muy mejorable) tenemos alternativas como Timber
, Sage
u otros, que nos proporcionan otra manera de escribir php
más moderna y limpia
Esto aplicaría sólo cuando queramos mezclar php
con html
, y vale mucho la pena utilizar esos entornos porque en general nos proporcionan experiencias de desarrollo (en WordPress y compañía) mucho mejores que utilizar php
sin más
Variables
Cada lenguaje ofrece un arsenal de variables y aunque es útil conocerlas todas, al final lo más rápido es asegurarse de cómo funcionan las "típicas"
Tenemos boolean, integer, float y string, lo habitual en cualquier lenguaje de programación
También tenemos los array y object, aunque con una nomenclatura que no es la misma que con JavaScript y que se presta a cierta confusión (si vienes de JavaScript)
Dos detalles importantes
-
Para definir cualquier variable hay que utilizar el símbolo
$
-
Con
php
es obligatorio;
utilizar el punto y coma al final de una frase
Luego tenemos el tema de las comillas
$var1 = true;
$var2 = 40;
$var3 = 40.67;
$var4 = 'qué tal estamos $var2'; // qué tal estamos $var2
$var5 = "qué tal estamos $var2"; // qué tal estamos 40
$var6 = "qué tal estamos ${var2}"; // qué tal estamos 40
$var7 = <<<FIN
hola
cómo vamos $var2?
espero que bien
FIN;
// hola
// cómo vamos 40?
// espero que bien
De algún modo reproducimos el comportamiento de las comillas '
, "
y `
de JavaScript
Arrays
Las arrays en php
funcionan muy parecido a JavaScript, excepto en que no son lo mismo
-
Un array en php es como un objeto en JavaScript, pero no puede tener métodos, y es iterable
-
Un objeto en php es como un objeto en JavaScript, iterable, y que sí puede tener métodos
Un array en php
tiene esta forma
$array = ["name" => "viñuelo", "rol" => "suscriptor"];
Si no queremos escribir las keys
podemos no hacerlo
// las dos expresiones son las mismas
$array = [0 => "viñuelo", 1 => "eustequio", 2 => "alfrisco"];
$array = ["viñuelo", "eustequio", "alfrisco"];
O podemos mezclarlo
$array = ["viñuelo", "eustequio", "alfrisco", "admin" => "jolgorio"];
Para luego acceder a los arrays
es como esperaríamos poder hacerlo (echo
sería similar a un console.log
), y también con una flecha simple cuando tengamos asignado una key
que no empiece por un número
echo $array[0]; // viñuelo
echo $array[1]; // eustequio
echo $array["admin"]; // jolgorio
echo $array->admin; // jolgorio
Luego para borrar, o reasignamos como null
o le damos pasaporte con unset $array[0];
o directamente a todo el array unset $array;
Hasta aquí, estos arrays de php
se parecen mucho a los objetos de JavaScript
Objetos
Un objeto en php
es un objeto dentro del contexto de programación orientada a objetos
Por lo tanto, para utilizar un objeto tenemos que definir la clase
del objeto en cuestión
class miClase {}
$miObjeto = new miClase;
La diferencia con el array
de antes? Que ahora podemos asignar métodos a un objeto, y también que la manera de acceder a los datos de un objeto es distinta
Lo vemos en un momento donde hablaré con más detalles de los objetos y las clases
Funciones
Las funciones aquí se definen como se definían antes en JavaScript, sin las fat arrow notation
function miFuncion () {
// lo que queramos
}
miFuncion();
Y podemos definir una variable como función
$miFunction = function miFuncion () {
// lo que queramos
}
Para recibir argumentos también podemos asignarles valores por defecto
function miFuncion ($msg = 'nada') {
echo $msg;
}
miFuncion('hola'); // hola
miFuncion(); // nada
Control y Loops
While, For, Foreach
Tenemos las estructuras típicas if-else y loops como los for y while clásicos
$array = ["viñuelo", "eustequio", "alfrisco", "admin" => "jolgorio"];
$i = 0;
while($i < count($array)) {
echo $array[$i];
$i++;
}
for($i = 0; $i < count($array); ++$i) {
echo $array[$i];
}
foreach ($array as &$value) {
echo $value;
}
El primer while nos imprime todos los elementos del array que tienen como índice un número, es decir que se salta el $array['admin']
Con el for de después tenemos la misma situación, sólo recorremos los índices numéricos del array
Y con el foreach recorremos todos los elementos, absolutamente todos
Cosas a comentar:
- Por qué
++$i
y$i++
?
Esto afecta a cuándo se ejecuta el operador ++
, si antes de leer la variable o después
En la estructura for
hace falta que se ejecute antes, mientras que si estamos en el bloque de while
nos da igual si antes o después
- En la línea
foreach ($array as &$value)
qué quiere decir el&$value
?
El signo &
quiere decir por referencia
Es decir, si no lo pusiésemos, $value
tendría una copia del valor de $array
, y si sí lo ponemos tendremos la dirección (la referencia)
Esto nos importa si queremos modificar el contenido del array
$array = ["viñuelo", "eustequio", "alfrisco", "admin" => "jolgorio"];
foreach ($array as &$value) {
$value = 'Mr ' . $value;
}
foreach ($array as $value) {
echo $value;
}
Aquí estoy cambiando el valor de $value
referido al valor de dentro del $array
, si no utilizara el signo &
simplemente estaría trabajando sobre una copia del valor por lo que el $array
original no se vería afectado
Luego tenemos las versiones funcionales de los loops que también tenemos en JavaScript
Array_map
Para iterar con una función que recibe el elemento de cada iteración
$array = ["viñuelo", "eustequio", "alfrisco", "admin" => "jolgorio"];
$newArray = array_map(function($el) {
return "el ${el}";
}, $array);
var_dump($newArray);
/*
array(4) {
[0]=> string(11) "el viñuelo"
[1]=> string(12) "el eustequio"
[2]=> string(11) "el alfrisco"
["admin"]=> string(11) "el jolgorio"
}
*/
Array_filter
Para iterar con una función que recibe el elemento de cada iteración, y filtrar en base a si retornamos true
o false
Aquí el array viene antes de la función
$array = ["viñuelo", "eustequio", "alfrisco", "admin" => "jolgorio"];
$newArray = array_filter($array, function($el) {
return $el !== "eustequio";
});
var_dump($newArray);
/*
array(3) {
[0]=> string(8) "viñuelo"
[2]=> string(8) "alfrisco"
["admin"]=> string(8) "jolgorio"
}
*/
Array_reduce
Y al igual que con el reduce
de JavaScript, aquí se trata de iterar sobre un array con un acumulador
$array = ["viñuelo", "eustequio", "alfrisco", "admin" => "jolgorio"];
$newArray = array_reduce($array, function($acc, $el) {
$acc .= $el . ', ';
return $acc;
}, 'estos son los nombres: ');
var_dump($newArray);
/*
string(64) "estos son los nombres: viñuelo, eustequio, alfrisco, jolgorio, "
*/
Clases (y objetos)
Ya hemos visto que los objetos son instancias de clases, y estos objetos pueden tener propiedades y métodos
Al final actúan como arrays
con métodos, aunque con toda una sintaxis algo farragosa ya que nos obliga a utilizar la programación orientada a objetos (OOP)
En general, una buena definición de cuándo la OOP es una buena elección comparándola con la programación funcional (FP) es la siguiente
- La OOP funciona mejor cuando tenemos funcionalidades definidas y objetos indefinidos
- La FP funciona mejor cuando tenemos objetos definidos y funcionalidades indefinidas
Otra definición la tenemos con el twit de Michael Feathers
La OOP nos simplifica el código encapsulando la partes móviles, la FP nos lo simplifica minimizando esas partes móviles
En cualquier caso, en OOP primero definimos el objeto
class miClase {}
$miObjeto = new miClase;
Luego añadimos propiedades y métodos
class Perro {
public $nombre = 'manual';
public function nombre() {
var_dump($this->nombre);
}
}
$miPerro = new Perro;
$miPerro->nombre(); // string(6) "manual"
var_dump($miPerro->nombre); // string(6) "manual"
Con la palabra public
decimos que esas propiedades y métodos están accesibles desde los objetos
Si hubiésemos declarado private
la propiedad $nombre
sólo podríamos acceder a ella a través del método nombre()
, no directamente con $miPerro->nombre;
Y con $this
nos referimos al propio objeto cuando estamos dentro de la clase
Para herencia de clases se utiliza la palabra extends
y simplemente se refiere a incorporar todo lo que la clase padre haya ya definido
class PerroTerrier extends Perro {
public $raza = 'terrier';
}
Y cuándo y por qué se usa static
?
class PerroTerrier extends Perro {
public static $raza = 'terrier';
}
Cuando sepamos que esa propiedad no va a cambiar nunca, podemos querer definirla como static
para así asegurarnos que nadie la podrá cambiar
Eso sí, cuando la definimos como static
esa propiedad deja de ser accesible desde el objeto (podemos acceder a ella desde la clase con el operador ::
o con el equivalente self
)
class Perro {
public $nombre = 'manual';
public function nombre() {
var_dump($this->nombre);
}
public function raza() {
var_dump($this->raza);
}
public function raza_self() {
// var_dump(self::raza); ERROR no hay ninguna propiedad definida raza en esta clase
}
}
class PerroTerrier extends Perro {
public static $raza = 'terrier';
public function raza_terrier() {
var_dump($this->raza);
}
public function raza_tipo_terrier() {
var_dump(self::$raza);
}
public function todo() {
var_dump($this->nombre . ' ' . PerroTerrier::$raza);
var_dump($this->nombre . ' ' . self::$raza); // equivalente
}
}
$perro = new PerroTerrier;
var_dump($perro->nombre); // string(6) "manual"
var_dump($perro->raza); // NULL no se puede acceder
$perro->nombre(); // string(6) "manual"
$perro->raza(); // NULL no se puede acceder
$perro->raza_terrier(); // NULL no se puede acceder
$perro->raza_tipo_terrier(); // string(7) "terrier"
var_dump(PerroTerrier::$raza); // string(7) "terrier"
$perro->todo(); // string(14) "manual terrier" string(14) "manual terrier"
Es decir, utilizamos 'static' cuando queremos fijar propiedades inherentes a la clase y no al objeto
Variables predefinidas
Esto vendría a ser parte de la API de php, esto es variables a las que siempre podremos acceder y que nos serán útiles para nuestro desarrollo
Por ejemplo $_SERVER
, $_FILES
, $_REQUEST
y $_SESSION
, que a medida que nos las encontramos iremos viendo su utilidad
El include
, include_once
, require
, require_once
El equivalente a import
en JavaScript (donde allí el require
utilizaba CommonJS
en lugar del más moderno ES6
)
La diferencia entre include
y include_once
es que aquí si utilizamos el primero nos lo importará sin importar si ya lo hemos hecho antes, mientras que en el segundo caso esto lo evitaremos (sin embargo el primer método es algo más rápido)
La diferencia entre el include
y el require
estriba en que el include
nos tolerará errores en los archivos que incluyamos, mientras que el require
no lo hará
Namespaces
Mientras que en React
todo nos viene encapsulado en su componente, en php
esto no es así y podemos tener colisiones de nombres
Para evitarlo utilizamos los namespaces
que lo que hacen es dar un espacio a nuestro código
Pero ese espacio (scope
) lo definimos con un nombre, y aquí también puede haber colisiones, por lo que hay que poner nombres relativamente largos y propios de nuestro código
Ah, y no puedes definir variables dentro de un namespace
, esto sólo sirve para las clases
namespace MiPluginEspecial;
// no funcionará como esperas, siempre se definirá en el scope global
$variable = 'hola';
function saludo() {
echo 'hola que tal';
}
// puedo llamar a la función directamente ya que estoy en el mismo espacio
saludo(); // hola que tal
// y si importo este archivo, lo llamaré desde el namespace
\MiPluginEspecial\saludo(); // hola que tal
// y también con la constante __NAMESPACE__
__NAMESPACE__.saludo(); // hola que tal
class miClase {
public $nombre = 'hola';
}
$miObjeto1 = new miClase;
$miObjeto2 = new \MiPluginEspecial\miClase;
var_dump($miObjeto1->nombre); // string(4) "hola"
var_dump($miObjeto2->nombre); // string(4) "hola"
Una vez tenemos el objeto todo nos queda encapsulado dentro del objeto en sí mismo
Type castings
Php es un lenguaje tipado, algo que JavaScript no es, pero que TypeScript (un JavaScript más amplio) sí lo es
Sin embargo los problemas de tipado nos los encontramos también en JavaScript cuando por ejemplo queremos sumar 2 números, pero uno de ellos está en formato string
const num1 = 10
const num2 = '10'
console.log(num1 + num2) // 1010
Cómo solucionamos esto? Haciendo un type casting, o lo que es lo mismo, convertir (en este caso) num2
a un número
En php
tenemos las mismas soluciones, con la ventaja de que en lugar de tener la situación de arriba tendremos un error y podremos localizar el bug al instante
$condicion = 'false';
if ($condicion)
echo 'hola';
else
echo 'adiós';
// hola
El problema aqui es que $condicion
es un string
y querríamos que fuera un booleano
$condicion = 'false';
if ((bool) $condicion)
echo 'hola';
else
echo 'adiós';
// hola
Pero así vuelve a fallar, y es que 'false'
es un string correcto y su casting a boolean
devuelve true
$condicion = '0';
if ($condicion)
echo 'hola';
else
echo 'adiós';
// adiós
if ((bool) $condicion)
echo 'hola';
else
echo 'adiós';
// adiós
Aquí pasa lo contrario, y es que el string
que tiene el contenido de '0'
se considera falso (un sinsentido)
Para solucionarlo podríamos utilizar la función empty()
, pero tampoco funcionaría, pero sí lo haría el is_null()
o el isset()
🙋♂️