Curso de PHP. Por Juan David Rodríguez García (juanda@juandarodriguez.es)

Curso PHP7

1 Licencia        4

2 Instalación y configuración de PHP en windows        4

2.1 Instalación de PHP con driver PDO para MSSQL Server        4

2.2 Instalación de Internet Information Server con PHP        6

2.3 Entornos de desarrollo integrados        7

3 Elementos básicos del lenguaje PHP        7

3.1 Sintaxis básica        7

3.2 Tipos        8

3.2.1 El tipo string        9

3.2.2 El tipo array        10

3.3 Variables        11

3.3.1 Variables predefinidas        12

3.3.2 Alcance de las variables        13

3.3.3 Variables variables        14

3.4 Constantes        14

3.5 Expresiones        14

3.6 Operadores        15

3.6.1 Precedencia        15

3.6.2 Tipos        15

3.7 Estructuras de control        17

3.7.1 La estructura foreach para iterar arrays e iterables        17

3.7.2 Sintaxis alternativa para estructuras de control        17

3.7.3 Include y require        18

3.7.4 Include_once y require_once        18

3.8 Funciones        19

3.9 Generadores        22

3.10 Variables predefinidas        22

3.11 Referencias        22

3.12 Errores        23

3.13 Excepciones        24

4 Ejercicio 1. Encriptación simétrica sencilla        25

5 Programación orientada a objetos        27

5.1 Declaración de una clase        27

5.1.1 Instanciación de un objeto        27

5.2 Atributos y métodos. Visibilidad        27

5.3 El atributo $this        29

5.4 Constructor y destructor        29

5.5 Herencia        30

5.6 Clases abstractas        31

5.7 Interfaces        32

5.8 Traits        33

5.9 El keyword final        34

5.10 Métodos mágicos        35

5.11 Autocarga de clases (autoloading)        36

6 Espacios de nombres        38

7 Ejercicio 2. Construcción de una librería modular para encriptar y desencriptar textos        40

8 Extensiones        41

9 Acceso al sistema de archivos        42

9.1 Abrir un fichero        43

9.2 Leer en un fichero        43

9.3 Escribir en un fichero        43

9.4 Cerrar un fichero        43

9.5 Leer un fichero completo y asignar su contenido a una string        44

9.6 Escribir una string a un fichero        44

9.7 Leer un fichero completo y asignarlo a un array        44

9.8 Leer un fichero y volcarlo a la salida estándar        45

10 Acceso a base de datos con PDO        45

10.1 Conexiones con PDO        46

10.2 Sentencias preparadas        47

10.3 Transacciones        48

10.4 Conexiones persistentes a base de datos        49

10.5 Conexión a bases de datos MSSQL Server        49

11 Ejercicio 3. Gestor de claves.        50

12 PHP en la web        51

12.1 Request        53

12.2 Response        54

12.3 Cookies        55

12.4 Session        56

12.5 Subida de archivos        58

13 Ecosistema PHP        60

13.1 PHP Framework Interop Group y las PSR        60

13.2 Composer        61

13.3 Testing con PHPUnit        61

13.4 Documentando con PHPDocumentor        62

13.5 Frameworks web        63

14 Ejercicio 4. Mejora del gestor de claves        64

15 Ejercicio 5. Gestor de clave web        64

16 Ejercicio final 6. Construcción de un framework web        64

17 Más cosas        64

17.1 Depurar con XDebug        64

17.2 PHP Thread Safe vs. Non Thread Safe        64

17.3 Máquina virtual de PHP7        65

17.4 PHP por dentro        65

17.5 Soluciones a los ejercicios        65


1 Licencia

La propiedad intelectual de esta obra pertenece a su autor: Juan David Rodríguez García (juanda@juandarodriguez.es)

Esta obra está bajo una Licencia Creative Commons Atribución-NoComercial-CompartirIgual 4.0 Internacional.

2 Instalación y configuración de PHP en windows

2.1 Instalación de PHP con driver PDO para MSSQL Server

Instalaremos la versión 7.1 pues microsoft aún no ha desarrollado el driver de MSSQL Server para PHP 7.2..

y  moverla a C:\php\extras\ssl, y descomentar y modificar la siguiente línea:

Para probar la instalación copiar el siguiente código en un archivo con extensión .php (prueba.php por ejemplo) en cualquier carpeta y desde una terminal de comandos ejecutarlo con el intérprete de PHP:

C:\Users\juanda> php prueba.php

El script realiza una conexión a una base de datos MSSQL Server ubicada en un servidor remoto (82.196.6.32), consulta la tabla productos y devuelve todas sus filas.

<?php
$dsn =
'sqlsrv:server=82.196.6.32;Database=prueba';
$user =
'kokolo';
$password =
'Pa$$prueba';

try
{
  $pdo_object =
new \PDO($dsn, $user, $password);
}
catch (PDOException $e)
{
 
echo 'Connection failed: ' . $e->getMessage() . PHP_EOL;
 
exit();
}

$sql =
"SELECT * from productos";
$pdo_statement_object = $pdo_object->prepare($sql);
$pdo_statement_object->execute();
$result = $pdo_statement_object->fetchAll();
print_r($result);

2.2 Instalación de Internet Information Server con PHP

Si no funciona abrir administrador de IIS y arrancar el servicio.

2.3 Entornos de desarrollo integrados

Podemos utilizar cualquier editor de textos para programar en PHP, pero siempre es más cómodo y resulta más eficaz utilizar algún entorno de desarrollo integrado (IDE). Los más utilizados en el mundo PHP son:

3 Elementos básicos del lenguaje PHP

Recursos:

3.1 Sintaxis básica

http://php.net/manual/en/language.basic-syntax.php

Un script PHP:

<?php
echo "Hello world";

// ... more code

echo "Last statement";

// the script ends here with no PHP closing tag

Solo se interpretan los bloques <?php ?>, el resto se vuelca a stdout

<p>This is going to be ignored by PHP and displayed by the browser.</p>
<?php echo 'While this is going to be parsed.'; ?>
<p>This will also be ignored by PHP and displayed by the browser.</p>

Se puede decidir condicionalmente qué bloques de texto no incluidos dentro de las etiquetas <?php ?> se vuelcan a stdout mediante la siguiente sintaxis que usa “:”

<?php if ($expression == true): ?>
 This will show if the expression is true.
<?php else: ?>
 Otherwise this will show.
<?php endif; ?>

Las instrucciones se separan con “;”. No se necesita “;” en la última línea del script si lleva la etiqueta ?>.

<?php
   
echo 'This is a test';
?>

<?php echo 'This is a test' ?>

<?php echo 'We omitted the last closing tag';

 

Los comentarios son como en C/C++ o Java:

<?php
   
echo 'This is a test'; // This is a one-line c++ style comment
   
/* This is a multi line comment
      yet another line of comment */

   
echo 'This is yet another test';
   
echo 'One Final Test'; # This is a one-line shell-style comment
?>

3.2 Tipos

http://php.net/manual/en/language.types.intro.php

4 tipos escalares:

4 compuestos

y 2 tipos especiales

3.2.1 El tipo string

Las cadenas son stream de bytes (caracter). PHP soporta un conjunto de 256 caracteres, por tanto no ofrece soporte Unicode nativo.

Detalles sobre el tipo string en PHP:

Las cadenas se pueden especificar como:

single quote: No se interpreta nada de lo que vaya dentro de la cadena

echo 'this is a simple string';

echo 'You can also have embedded newlines in
strings this way as it is
okay to do'
;

double quote: Se interpretan las variables y algunas secuencias de escape como “\n”.

$size = 20;
echo "Se han descargado $size bytes";

// da como resultado:

// Se han descargado 20 bytes

heredoc: Se interpretan las variables y algunas secuencias de escape como “\n”.

echo <<<EOT
My name is
"$name". I am printing some $foo->foo.
Now, I am printing some {
$foo->bar[1]}.
This should
print a capital 'A': \x41
EOT;

nowdoc: No se interpreta nada

echo <<<'EOD'
Example of
string
spanning multiple lines
using nowdoc syntax.
EOD;

3.2.2 El tipo array

Los arrays son conjunto de elementos pares clave-valor. En PHP los arrays se pueden utilizar para implementar listas, vectores, tablas hash y cualquier otra estructura de datos que involucre a conjunto de datos o colecciones. Los elementos de un array pueden tener como valor otro array, con lo que podemos implementar estructuras anidadas.

Se pueden definir de varias formas:

con el elemento del lenguaje array:

$a = array(
   
"foo" => "bar",
   
"bar" => "foo",
);

con los caracteres “[“ y “]”:

$a = [
   
"foo" => "bar",
   
"bar" => "foo",
];

Las claves pueden ser integer o string, si no lo son se produce un cast a uno de los dos tipos. Si las claves son integers consecutivos los arrays se pueden definir también así:

$a = array("bar", "foo");

$array2 = [
"bar", "foo"];

// es equivalente a

$array =
array(0 => "bar", 1 => "foo");

Si queremos examinar un array podemos usar la función var_dump:

echo var_dump($a);

Los arrays e iterables se pueden recorrer con foreach:

$iterable = ["foo", "var"];

foreach ($iterable as $value) {
        
// ...
}

Un ejemplo de array :

$a = [
 
"usuarios" => [
      [
         
"nombre" => "juan",
         
"apellido" => "rodríguez",
      ],
      [
         
"nombre" => "josé",
         
"apellido" => "garcía",
      ],
  ],
 
"rios" => ["guadalquivir", "guadiana", "tajo"],
];

Los elementos de un array se acceden indicando la clave entre los caracteres “[“ y  “]” aplicados a la variable que contienen el array. Por ejemplo, en el código anterior:

echo $a["usuarios"][1]["nombre"];

// Da lugar a
// josé

3.3 Variables

http://php.net/manual/en/language.variables.php

Se definen precediendo con “$” al nombre de la variable. Son case sensitive. Deben comenzar por una letra o “_” seguido por letras, “_” o números. Una letra es cualquier varacter ASCII extendido.

<?php
$var =
'Bob';
$Var =
'Joe';
echo "$var, $Var";      // outputs "Bob, Joe"

$
4site = 'not yet';     // invalid; starts with a number
$_4site =
'not yet';    // valid; starts with an underscore
$täyte =
'mansikka';    // valid; 'ä' is (Extended) ASCII 228.
?>

Por defecto se asignan por valor. Cuando se asigna una expresión a una variable, el valor de la expresión es copiado a dicha variable. Para realizar una asignación por referencia se utiliza el caracter “&”, precediendo a la expresión.

<?php

$a =
"Hola";
$b = $a;
$a =
"Adios";

echo "$a\n$b\n";

// Da lugar a
// Adios
// Hola

$a =
"Hola";
$b = &$a;
$a =
"Adios";

echo "$a\n$b\n";

// Da lugar a
// Adios
// Adios

Una variable que no se ha definido siempre tiene asociado el valor NULL:

<?php
echo is_null($a);

// Da lugar a TRUE

3.3.1 Variables predefinidas

PHP ofrece algunas variables predefinidas con valores que tienen que ver con el contexto de ejecución del script. Algunos ejemplo:

$argc, número de argumentos pasados al script

$argv, array con los argumentos pasados al script

$_REQUEST, array con los parámetros de la petición HTTP en un contexto web

Aquí está la lista completa de variables predefinidas.

http://php.net/manual/en/reserved.variables.php

3.3.2 Alcance de las variables

Pueden ser globales, locales a una función y estáticas.  

Para acceder a las variables globales desde dentro de una función se usa el keyword global. También se puede usar el array predefinido $GLOBALS.

Las variables estáticas son también locales pero no pierden el valor cuando se sale de la función,  de manera que al volver a entrar en ella mantienen el valor que tenían en la última ejecución de la función.

<?php

$a =
1; /* global scope */

function test1()
{
 
return $a; /* reference to local scope variable */
}

function test2()
{
 
global $a;
 
return $a; /* reference to local global variable */
}

function test3()
{
 
return $GLOBALS['a']; /* reference to local global variable */
}

echo test1();
echo PHP_EOL;
echo test2();
echo PHP_EOL;
echo test3();

// Da lugar a
// NULL
// 1
// 1

3.3.3 Variables variables

Podemos usar repetidamente “$” para obtener variables que son variables.

<?php

$a =
"hello";

$$a =
"world";

echo $a;
echo PHP_EOL;
echo $hello;

// Da lugar a
// hello
// world

3.4 Constantes

http://php.net/manual/en/language.constants.php

Son identificadores que referencian a un valor que no cambia. Se pueden definir de dos formas:

define('CONSTANTE', 'hola que tal');

const CONSTANTE2 = 'pues muy bien';

Constantes predefinidas

Constantes mágicas: su valor depende de la parte del script donde se está usando, por ejemplo __FILE__ tiene como valor asociado la ruta del script donde se está usando.  

3.5 Expresiones

http://php.net/manual/en/language.expressions.php

Una expresión es cualquier cosa que tenga un valor. O dicho de otra manera, cualquier cosa que pueda evaluarse.

PHP es un lenguaje orientado a la “expresión” con lo que prácticamente cualquier cosa en PHP es una expresión.

Un ejemplo de código PHP para identificar expresiones:

<?php

$a =
56;

function myfunc($x){
 
return 2*x;
}

$a++;

$a = $a - myfunc($a);

$a ==
24;

$b = ($a ==
24)? 'a es 24': 'a no es 24';

3.6 Operadores

http://php.net/manual/en/language.operators.php

Son elementos que toman uno o varios valores y lo transforman en otro.

3.6.1 Precedencia

http://php.net/manual/en/language.operators.precedence.php

3.6.2 Tipos

Operador “==” vs “===” y “!=” vs “!==”:

Cuando se utilizan dos veces el caracter “=” se compara también el tipo.

<?php

echo 1 == "1";
echo PHP_EOL;
echo 1 === "1";
echo PHP_EOL;

// Da lugar a
// True
// False

Podemos ejecutar comandos del sistema operativo con “`”:

$output = `ls -al`;
echo "<pre>$output</pre>";

Concatenación de cadenas:

$a = "Hello ";
$b = $a . "World!"; // now $b contains "Hello World!"

$
a = "Hello ";
$a .= "World!";     // now $a contains "Hello World!"

Podemos saber la clas a la que pertenece un objeto con instanceof:

class MyClass
{
}

class NotMyClass
{
}
$a =
new MyClass;

var_dump($a
instanceof MyClass);
var_dump($a
instanceof NotMyClass);

3.7 Estructuras de control

http://php.net/manual/en/language.control-structures.php

Cualquier script PHP está construido a partir de una serie de declaraciones (statments). Una declaración puede ser una asignación, una llamada a función, un ciclo, una instrucción condicional o incluso una instrucción que no hace nada (una instrucción vacía). Las declaraciones generalmente terminan con un punto y coma. Además, los enunciados se pueden agrupar en un grupo de enunciados encapsulando un grupo de enunciados con llaves. Un grupo de enunciados también es una afirmación en sí misma.

Las estructuras de control permiten decidir qué declaraciones se ejecutan en función de qué condiciones (condiciones), cuantas veces se deben ejecutar (bucles), cuando cortarlas, a que declaración saltar, etcétera.

3.7.1 La estructura foreach para iterar arrays e iterables

$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
   $value = $value *
2;
}

3.7.2 Sintaxis alternativa para estructuras de control

Esta sintaxis alternativa es especialmente útil cuando PHP se utiliza mezclado con código HTML:

<?php if ($a == 5): ?>
A is equal to 5
<?php endif; ?>

<table>
       
<tr>
           
<th>alimento (por 100g)</th>
           
<th>energía (Kcal)</th>
           
<th>grasa (g)</th>
       
</tr>
       
<?php foreach ($alimentos as $alimento) :?>
       
<tr>
           
<td><a href="index.php?ctl=ver&id=<?php echo $alimento['id']?>">
                   
<?php echo $alimento['nombre'] ?>
               
</a>
       
</td>
           
<td><?php echo $alimento['energia']?></td>
           
<td><?php echo $alimento['grasatotal']?></td>
       
</tr>
       
<?php endforeach; ?>

   
</table>

3.7.3 Include y require

Ambas sirven para incluir y evaluar un script dentro de otro script, la diferencia es que require, en caso de fallo, lanza un error fatal.

vars.php
<?php

$color =
'green';
$fruit =
'apple';

?>

test.php
<?php

echo "A $color $fruit"; // A

include 'vars.php';

echo "A $color $fruit"; // A green apple

?>

Una buena costumbre es usar rutas absolutas para especificar los scripts que se incluyen, el problema es que eso hace que los scripts dejen de funcionar si los colocamos en otra ubicación. Podemos evitar este problema usando la constante mágica __DIR__:

include __DIR__.'/../vars.php';

3.7.4 Include_once y require_once

Funcionan igual que include y require, pero si ya se ha incluido en otro script el mismo script solicitado, no se vuele a cargar.

3.8 Funciones

http://php.net/manual/en/language.functions.php

La sintaxis de una función:

<?php
function foo($arg_1, $arg_2, /* ..., */ $arg_n)
{
   
echo "Example function.\n";
   
return $retval;
}

El nombre debe cumplir lo mismo que las variables: comenzar por una letra o “_” seguido de letras, “_” o números.

Todas las funciones pertenecen al global scope, aunque hayan sido definidas dentro de otra función.

Por defecto los valores de los argumentos se pasan por valor, aunque también se puede usar el paso por referencia usando el operador “&”, y el paso de argumentos por defecto.

function increment(&$x){
   $x = $x +
1;
   
// No hace falta devolver nada por que esta función cambia el valor
   
// de la variable que se pasa como argumento. Es lo que se llama un
   
// efecto colateral, y por lo general no es buena idea hacer esto.
}

function makecoffee($type = "cappuccino")
{
   
return "Making a cup of $type.\n";
}

Cuando se usan argumentos por defecto, estos deben colocarse en la última posición de la lista de argumentos.

Opcionalmente podemos usar la declaración de tipos, la cual es muy recomendable para el desarrollo de un código más robusto y defensivo. También mejoran la autodocumentación de las funciones.

function areaTriangulo(float $b, float $h) : float {
        
$area = $b*$h/2;
        
return $area;
}

Otra característica de las funciones en PHP es la posibilidad de indicar un número variable de argumentos:

<?php

function sum(...$numbers) {
  $acc =
0;
  foreach ($numbers
as $n) {
      $acc += $n;
  }
 
return $acc;
}

echo sum(
1,2,3,4,5);

// Da lugar a
// 15

El operador “...” también se puede usar en la llamada a funciones con un nº variable de argumentos para pasar un array como argumento y desempaquetarlo:

$a = [1,2,3,4,5];
echo sum(...$a);

Si todos los argumentos variables son de un determinado tipo, podemos usar la declaración de tipos sobre ellos:

function total_intervals($unit, DateInterval ...$intervals) {
   $time =
0;
   foreach ($intervals
as $interval) {
       $time += $interval->$unit;
   }
   
return $time;
}

Algo que a veces resulta muy útil es el hecho de que podemos llamar a una función usando una variable que tenga como valor el nombre de la función:

$func = 'foo';
$func();        // This calls foo()

$
func = 'bar';
$func('test');  // This calls bar()

Las funciones anónimas, como su nombre indica, son funciones sin nombre. Se pueden pasar como argumento a otras funciones. Esto es útil cuando una función necesita, en alguna parte, necesita hacer un cálculo específico en función del uso que se le está dando.

function piezaEntra(callable $area, float $areaMin, float ...$values): bool {
  $a = $area(...$values);

 
return $a < $areaMin;
}

// Para una pieza triangular
echo piezaEntra(function($b, $h){
 
return $b*$h/2;
},
125, 10,20);

echo PHP_EOL;


// Para una pieza cuadrada
echo piezaEntra(function($l){
 
return $l*$l;
},
125, 5);

Se pueden usar también como clausuras, pero hay que indicarle las variables que se quieren “clausurar”.

public function getTotal($tax)
   {
       $total =
0.00;
       
       $callback =
           
function ($quantity, $product) use ($tax, &$total)
           {
               $pricePerItem = constant(
__CLASS__ . "::PRICE_" .
                   strtoupper($product));
               $total += ($pricePerItem * $quantity) * ($tax +
1.0);
           };
       
       array_walk(
$this->products, $callback);
       
return round($total, 2);
   }

3.9 Generadores

http://php.net/manual/en/language.generators.php

Los generadores ofrecen una manera de aprovechar la memoria y la complejidad que puede resultar de la necesidad de usar arrays muy grandes, siempre que los valores de estos arrays puedan computarse de alguna manera. El generador es un iterable que se puede recorrer con un foreach pero con la particularidad que cada valor es calculado en el momento en que se necesita.

function pares($limite = 10){
 
for($i=0; $i < $limite; $i++){
     
if ($i % 2 == 0) yield $i;
  }
}

foreach(pares() as $par){
 
echo $par . PHP_EOL;
}

// Da lugar a
// 0,2,4,6,8

foreach(pares(4) as $par){
 
echo $par . PHP_EOL;
}

// Da lugar a
// 0, 2

3.10 Variables predefinidas

http://php.net/manual/en/reserved.variables.php

3.11 Referencias

http://php.net/manual/en/language.references.php

En PHP el valor de una variable y el nombre de una variables son cosas distintas. De manera que un mismo valor puede tener varios nombre (referencia). Las referencia no son punteros.

Asignar por referencia:

$a =& $b;

Pasar por referencia:

function foo(&$var)
{
   $var++;
}

$a=
5;
foo($a);
// $a is 6 here

Devolver por referencia.

class foo {
   
public $value = 42;

   
public function &getValue() {
       
return $this->value;
   }
}

$obj =
new foo;
$myValue = &$obj->getValue();
// $myValue is a reference to $obj->value, which is 42.
$obj->value =
2;
echo $myValue;

Eliminar referencias.

$a = 1;
$b =& $a;
unset($a);

3.12 Errores

Tipos de errores: http://php.net/manual/en/errorfunc.constants.php

Los tipos de errores que el intérprete de PHP lanza se pueden definir mediante la directiva error_reporting del fichero de configuración php.ini, o en tiempo de ejecución mediante la función error_reporting().

A la hora de mostrarlo se utiliza la directiva display_errors, la cual debería estar deshabilitada en un entorno de producción.

También se puede (y se debe) volcar a un sistema de log (syslog, fichero, ….) los errores que aparecen en la ejecución de los scripts. Esto se habilita con la directiva log_errors y dónde se vuelcan con la directiva error_log.

3.13 Excepciones

http://php.net/manual/en/language.exceptions.php

El mecanismo de excepciones está basado en los típico bloques try-catch-finally.

El código que se quiere controlar se mete en el bloque try, y se añaden tantos bloques catch como tipos de excepciones queramos controlar. Opcionalmente se añade un bloque finally que se ejecutará en todos los casos.

function inverse($x) {
   
if (!$x) {
       
throw new Exception('Division by zero.');
   }
   
return 1/$x;
}

try {
   
echo inverse(5) . "\n";
   
echo inverse(0) . "\n";
}
catch (Exception $e) {
   
echo 'Caught exception: ',  $e->getMessage(), "\n";
}

// Continue execution
echo "Hello World\n";

// Da lugar a

// 0.2
// Caught exception: Division by zero.
// Hello World

Podemos crear nuestras propias excepciones sin más que crear una clase que extienda de la clase base Exception:

class MyException extends Exception { }

Las excepciones se pueden lanzar mediante la keyword throw:

class MyException extends Exception { }

class MyOtherException extends Exception { }

class Test {
   public function testing() {
       
try {
           
throw new MyException();
       }
catch (MyException | MyOtherException $e) {
           var_dump(get_class($e));
       }
   }
}

$foo =
new Test;
$foo->testing();

// Da lugar a

// string(11) "MyException"

4 Ejercicio 1. Encriptación simétrica sencilla

Desarrollar una aplicación CLI que sirva para codificar o decodificar una cadena de caracteres según los siguientes requisitos:

Sugerencias:

function _readline($prompt){
 
if (PHP_OS == 'WINNT') {
     
echo $prompt . ' ';
      $line = stream_get_line(STDIN,
1024, PHP_EOL);
    }
else {
      $line = readline($prompt .
' ');
    }

   
return $line;
}

$a = "Hola";
echo $a[3];
// Da lugar a
// a

5 Programación orientada a objetos

5.1 Declaración de una clase

Se hace con el keyword class.

class Usuario {
}

5.1.1 Instanciación de un objeto

Se hace con el keyword new.

$u = new Usuario();

5.2 Atributos y métodos. Visibilidad

Los atributos y los métodos pueden ser público, protected, privados o estáticos.

Fuera del objeto significa que se puede acceder desde una instancia de de la clase (objeto).

Dentro del objeto significa que solo se puede acceder desde funciones pertenecientes al

objeto.

Los atributos y métodos de un objeto se acceden con el operador “->”:

$usuario->getUsername();
$usuario->id;

Los atributos y métodos estáticos, se accede con el operador “::

Usuario::setHashPassword('kakalaka');

Como curiosidad, a este operador se le llama “Paamayim Nekudotayim”. Significa “doble dos puntos” en hebreo.

Un ejemplo de clase:

class Usuario {

 
public $id; // Se puede usar desde fuera del objeto
 
private $username; // Solo se puede usar dentro del objeto
 
private $password; // Solo se puede usar dentro del objeto

 
public function __construct($u = NULL){
     
$this->username = $u;
     
$this->id = Usuario::generateUniqueId();
  }
   

   // Se puede acceder desde fuera. Es una función de la clase, no

   // hay que instanciar objeto para invocarla
 
static public function hashPassword($p)
  {
     
return hash('sha256', $p);
  }
  // Solo se puede llamar desde dentro de la clase (como en el método

   // setPassword(). Es una función de la clase, no hay que instanciar    

   // objeto para invocarla
 
static private function generateUniqueId(){      
     
return uniqid();
  }

   // Es una función (método) de los objetos instanciados a partir de

   // la clase que puede ser usada desde fuera.
 
public function getUsername()
  {
     
return $this->username;
  }

   // Es una función (método) de los objetos instanciados a partir de

   // la clase que puede ser usada desde fuera.

   public function setUsername(string $username)
  {
     
$this->username = $username;
  }
 

   // Es una función (método) de los objetos instanciados a partir de

   // la clase que puede ser usada desde fuera.
 
public function setPassword(string $p)
  {
     
$this->password = Usuario::hashPassword($p);
  }
 

   // Es una función (método) de los objetos instanciados a partir de

   // la clase que puede ser usada desde fuera.
 
public function getPassword(){
     
return $this->password;
  }

}


$u =
new Usuario();
$u->setUsername(
'juanda');
$u->setPassword(
'kaka');
echo $u->id;
echo PHP_EOL;
echo $u->getUsername();
echo PHP_EOL;
echo $u->getPassword();

// Da lugar a
// 5a94274d39246
// juanda
// 3a38f42cfb8f4ad7419c4c4a5bdbbf2f447ebf05d6dce5de5daa670469dc0e56

5.3 El atributo $this

Es un atributo especial que hace referencia al propio objeto. Se usa en las funciones de la clase para hacer referencia a las instancias que de ella se hagan. En el ejemplo anterior se ve como se usa.

5.4 Constructor y destructor

El constructo es un método público especial que se invoca automáticamente cuando se crea una instancia de la misma.

public function __construct(...$args){
   
// código
}

El destructor es un método es un método especial que se invoca tan pronto como no haya otras referencias al objeto en particular.

public function __destruct(){
   
// código
}

Es útil para cerrar conexiones a bases de datos y otros recursos. También son útiles para persistir el estado de un objeto antes de que se destruya, en caso de que sea necesario dicha persistencia.

5.5 Herencia

PHP permite herencia simple, esto es: una clase no puede extender a varias clases a la vez. Pero si que se pueden definir jerarquías de clases todo lo profundas que se quiera. Es decir, una clase puede extender a una clase que a su vez extiende a otra clase y así tantas veces como se quiera.

La extensión se hace con la keyword extends.

class Gato extends Animal {
}

Todos los atributos y método públicos y protected de la clase padre son heredados por las clases hijas. Los atributos y métodos privados, NO.

Los objetos, igual que cualquier otra variable, se asignan por valor. Pero hay que matizar algo. Las variables que contienen objetos, no son referencias al objeto en sí, si no a un identificador del objeto que es una referencia al objeto. Entonces cuando se asigna una variable a otra variable que fue iniciada con un objeto, lo que se copia en realidad es el identificador del objeto. Por eso las dos variables apuntan a través de cada copia del identificador al mismo objeto. Por ello, la consecuencia es que los objetos son asignados, en la práctica, por referencia.

// Se crea un objeto Usuario, un identificador de objeto y este último se asigna a $a
$a =
new Usuario() ;

// Se copia el contenido de $a, es decir el identificador del objeto y se asigna la copia a $b
$b = $a;

$a->setNombre(
'juanda');

echo $b->getNombre();

// Da lugar a:
// juanda

5.6 Clases abstractas

Las clase abstractas no pueden ser instanciadas. Solo las clases que la extienden pueden serlo.

Las clases abstractas pueden definir métodos bien definidos, es decir con código y también métodos abstractos, los cuales indican que las clases que la extiendan deben implementar  dichos métodos, esto es, deben declarar el método y su código.

Ejemplo:

abstract class Persona {

 
protected $nombre;

 
abstract function setPassword(string $p );

 
public function setNombre($n){
     
$this->nombre = $n;
  }

 
public function getNombre()
  {
     
return $this->nombre;
  }

}

class Usuario extends Persona {

 
public $id;
 
private $username;
 
private $password;

 
public function __construct($u = NULL){
     
$this->username = $u;
     
$this->id = Usuario::generateUniqueId();
  }

 
static public function hashPassword($p)
  {
     
return hash('sha256', $p);
  }

 
static private function generateUniqueId(){      
     
return uniqid();
  }

 
public function getUsername()
  {
     
return $this->username;
  }

 
public function setUsername(string $username)
  {
     
$this->username = $username;
  }

 
public function setPassword(string $p)
  {
     
$this->password = Usuario::hashPassword($p);
  }

 
public function getPassword(){
     
return $this->password;
  }

}


$u =
new Usuario();
$u->setNombre(
'Juan David');
$u->setUsername(
'juanda');
$u->setPassword(
'kaka');
echo $u->getNombre();
echo PHP_EOL;
echo $u->id;
echo PHP_EOL;
echo $u->getUsername();
echo PHP_EOL;
echo $u->getPassword();
echo PHP_EOL;
echo $u->id;

5.7 Interfaces

Son plantillas de clases. Las interfaces declaran signaturas de métodos que cualquier clase que la implemente debe definir.

Interface FiguraGeometrica {
 
public function getArea(): float;
}

class Cuadrado implements FiguraGeometrica {

 
public $lado;

 
public function getArea(): float
  {
     
return $this->lado * $this->lado;
  }
}

$c =
new Cuadrado();
$c->lado =
5;
echo $c->getArea();

// Da lugar a

// 25

5.8 Traits

Se trata de un elemento que “alivia” el problema de la inexistencia de la herencia múltiple. Un trait es un conjunto de métodos que pueden ser incorporados a cualquier clase. Una clase puede incorporar tantos traits como se desee.

trait ezcReflectionReturnInfo {
   function getReturnType() {
/*1*/ }
   function getReturnDescription() {
/*2*/ }
}

class ezcReflectionMethod extends ReflectionMethod {
   use ezcReflectionReturnInfo;
   
/* ... */
}

class ezcReflectionFunction extends ReflectionFunction {
   use ezcReflectionReturnInfo;
   
/* ... */
}

Múltiples traits:

trait Hello {
   
public function sayHello() {
       
echo 'Hello ';
   }
}

trait World {
   
public function sayWorld() {
       
echo 'World';
   }
}

class MyHelloWorld {
   
use Hello, World;
   
public function sayExclamationMark() {
       
echo '!';
   }
}

$o =
new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();

5.9 El keyword final

Mediante este keyword se le indica:

Ejemplo en un método:

class BaseClass {
 
public function test() {
     
echo "BaseClass::test() called\n";
  }
 
 
final public function moreTesting() {
     
echo "BaseClass::moreTesting() called\n";
  }
}

class ChildClass extends BaseClass {
 
public function moreTesting() {
     
echo "ChildClass::moreTesting() called\n";
  }
}
// Results in Fatal error: Cannot override final method BaseClass::moreTesting()

Ejemplo en una clase:

final class BaseClass {
 
public function test() {
     
echo "BaseClass::test() called\n";
  }

 
// Here it doesn't matter if you specify the function as final or not
 
final public function moreTesting() {
     
echo "BaseClass::moreTesting() called\n";
  }
}

class ChildClass extends BaseClass {
}
// Results in Fatal error: Class ChildClass may not inherit from final class (BaseClass)

5.10 Métodos mágicos

Los nombres de función __construct (), __destruct (), __call (), __callStatic (), __get (), __set (), __set (), __setup (), __sleep (), __wakeup (), __toString (), __invoke () , __set_state (), __clone () y __debugInfo () son mágicos en las clases de PHP. No puede tener funciones con estos nombres en ninguna de sus clases a menos que desee la funcionalidad mágica asociada a ellas.

De entre todos los método anteriores 3 son los más usados.

class Cuadrado implements FiguraGeometrica {

 
public $lado;

 
public function getArea(): float
  {
     
return $this->lado * $this->lado;
  }

 
public function __toString()
  {      
     
return "Soy un cuadrado de " . $this->lado . " unidades de lado";
  }
}

$c =
new Cuadrado();
$c->lado =
5;
echo $c->getArea();
echo PHP_EOL;
echo $c;

// Da lugar a

// 25

// Soy un cuadrado de 5 unidades de lado

5.11 Autocarga de clases (autoloading)

Cuando desarrollamos una aplicación haciendo uso de clases, es normal que el número de estas sea bastante grande.

También es conveniente, por cuestiones de organización, que cada clase esté definida en un sólo fichero, cuyo nombre suele coincidir con el nombre de la clase.

El problema de esto es que el nº de requires (o includes) y su control se dispara y supone un frenazo en la agilidad del desarrollo.

Afortunadamente existe en PHP un mecanismo de autocarga que permite eliminar el uso de requires. Este mecanismo funciona de la siguiente manera.

Cuando se instancia una clase, el intérprete de PHP busca si existe alguna función de autocarga registrada en el sistema. Sí la hay,  la invoca pasándole como argumento el nombre de la clase. En caso de que se haga un require con éxito termina el proceso, en caso contrario invoca a la próxima función de autocarga registrada. El proceso continúa hasta que se haga algún require con éxito o ya no haya más funciones de autocarga registradas.

Según lo dicho en el párrafo anterior, las funciones de autocarga deben tener como argumento el nombre de la clase.

Es responsabilidad del desarrollador definir estas funciones. Por tanto las estrategias para autocargar son infinitas. No obstante, el grupo de interoperabilidad de PHP (PHP-FIG) ha definido unos estándares donde se establece la manera correcta de realizar la autocarga (al menos la que ellos consideran mejor).

La autocarga, en combinación con los espacios de nombres, ofrecen el mecanismo básico para la organización modular y escalable del código PHP.

lib1/Cuadrado.php

<?php

class Cuadrado {

 
public function __toString(){
     
return "Soy un cuadrado";
  }
}

lib2/Gato.php

<?php

class Gato{

 
public function __toString(){
     
return "Soy un gato";
  }
}

index.php

spl_autoload_register(function($name){

  $file =
__DIR__ . '/lib1/' . $name . '.php';
 
if(!file_exists($file)) return;
 
require $file;
});

spl_autoload_register(
function($name){
  $file =
__DIR__ . '/lib2/' . $name . '.php';
 
if(!file_exists($file)) return;
 
require $file;
});


$c =
new Cuadrado();

$p =
new Gato();

echo $c;
echo PHP_EOL;
echo $p;

// Da lugar a

// soy un cuadrado

// soy un gato

6 Espacios de nombres

http://php.net/manual/en/language.namespaces.php

Los espacios de nombre evitan la colisión de nombres de clases y permiten estructurar de manera jerárquica las clases de PHP. La idea es similar a los packages de Java o de Python.

Un espacio de nombres, sintácticamente, se corresponde con un conjunto de nombres separados por el carácter “\”, el cual actúa como separador y organizador de la jerarquía. Los espacios de nombres constituyen una estructura de árbol, exactamente igual que la estructura de directorios de un sistema de archivos (pero no es una estructura de directorios, cuidado que sólo es un símil).

Espacios de nombre válidos serían:

Symfony
Symfony\Component\
Console\Command
Symfony\Component\Routing
Doctrine\ORM
Doctrine\ORM\Query
Slim

Los espacios de nombre se aplican a los scripts PHP mediante el keyword namespace, en la primera línea del script.

<?php
namespace Geometria\Plana

class Cuadrado {
}

Cuando se aplica un namespace a un script PHP, todas las clases de ese fichero tienen por nombre completo el namespace más el nombre que se ha usado en la definición de la clase. En el código anterior, el nombre completo de la clase no sería Cuadrado, si no \Geometria\Plana\Cuadrado. Obsérvese el carácter “\” al principio del  nombre. Esto es algo que puede liar al principio, aunque en el script el namespace se ha definido sin “\” al principio, el nombre de la clase comienza por “\”.

Si desde un script, requerimos el fichero anterior, por el mecanismo de autocarga o directamente, podemos instanciar un objeto de la clase \Geometria\Plana\Cuadrado así:

$c = new \Geometria\Plana\Cuadrado();

Como el nombre completo de las clases con namespaces resulta muy largo, podemos utilizar el keyword use (que es similar a import en Java) al principio del script con el nombre completo de la clase (esta vez sin el “\” del principio). De manera que en ese script se puede utilizar el nombre de la clase tal y como se definió originalmente.

<?php
namespace Otro\Namespace; // esto no es necesario para utilizar 'use'

use Geometria\Plana\Cuadrado;

$c =
new Cuadrado();

 

En ocasiones puede que sea útil usar una parte del namespace únicamente, sin llegar al último nombre de la jerarquía. En ese caso debemos usar un alias para instanciar clases que pertenezcan a ese namespace.

<?php
namespace Otro\Namespace;

use Geometria\Plana as GeoPlana;

$c =
new GeoPlana\Cuadrado();

Todas las clases pertenecen a algún espacio de nombres, aquellas que están definidas en ficheros que no definen namespace, pertenecen al espacio de nombres raíz (\).

Los espacios de nombre junto con el mecanismo de autocarga ofrecen muchas posibilidades para organizar adecuadamente el código de una manera modular y escalable. Cuando se usa la autocarga, el argumento que se pasa a las funciones de autocarga es el nombre completo de la clase, con todo el espacio de nombre. Esto permite que podamos asociar espacios de nombres a directorios donde buscar las clases de dicho espacio. Veremos más adelante la solución que el Grupo de Interoperabilidad de PHP (PHP-FIG) ha propuesto en un standard (PSR-4) para realizar la autocarga de clases con espacios de nombre, y que es la base para la instalación automatizada de librerías de terceros con composer, una herramienta imprescindible para trabajar con PHP profesionalmente.

7 Ejercicio 2. Construcción de una librería modular para encriptar y desencriptar textos

En este ejercicio vas a construir una jerarquía de clases  para encriptar y desencriptar textos con clave simétrica y utilizando varios algoritmos distintos de encriptación. Una vez construida la librería desarrollar una aplicación como la del 4 Ejercicio. Encriptación simétrica sencilla, pero que además de la clave pida el algoritmo de cifrado.

Los requisitos son los siguientes:

Nota: SimpleCrypter es el encriptador que hemos desarrollado anteriormente.

Sugerencias:

El siguiente código te va a venir bien para construir las funciones de encriptación/desencriptación.

<?php
/**
* simple method to encrypt or decrypt a plain text string
* initialization vector(IV) has to be the same when encrypting and decrypting
*
* @param string $action: can be 'encrypt' or 'decrypt'
* @param string $string: string to encrypt or decrypt
*
* @return string
*/

function encrypt_decrypt($action, $string) {
  $output =
false;
 
//$encrypt_method = "AES-256-CBC";
 
//$encrypt_method = "CAMELLIA-128-CBC";
  $encrypt_method =
"des-ede3-cfb";
 
  $secret_key =
'This is my secret key';
  $secret_iv =
'This is my secret iv';
 
// hash
  $key = hash(
'sha256', $secret_key);
 
 
// iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
  $iv = substr(hash(
'sha256', $secret_iv), 0, 16);
 
if ( $action == 'encrypt' ) {
      $output = openssl_encrypt($string, $encrypt_method, $key,
0, $iv);
      $output = base64_encode($output);
  }
else if( $action == 'decrypt' ) {
      $output = openssl_decrypt(base64_decode($string), $encrypt_method, $key,
0, $iv);
  }
 
return $output;
}
print_r(openssl_get_cipher_methods());
$plain_txt =
"This is my plain text";
echo "Plain Text =" .$plain_txt. "\n";
$encrypted_txt = encrypt_decrypt(
'encrypt', $plain_txt);
echo "Encrypted Text = " .$encrypted_txt. "\n";
$decrypted_txt = encrypt_decrypt(
'decrypt', $encrypted_txt);
echo "Decrypted Text =" .$decrypted_txt. "\n";
if ( $plain_txt === $decrypted_txt ) echo "SUCCESS";
else echo "FAILED";
echo "\n";

Y el siguiente código te resuelve el tema de la autocarga:

<?php
spl_autoload_register(
function ($class) {
         
  $sep = (strtoupper(substr(PHP_OS,
0, 3)) === 'WIN')? '\\' : '/';

  $base_dir =
__DIR__ . $sep;

  $file = $base_dir . str_replace(
'\\', $sep, $class) . '.php';

 
if (file_exists($file)) {
     
require $file;
  }
});

8 Extensiones

Las extensiones son librerías compiladas que añaden nuevas funcionalidades al intérprete de PHP. Las extensiones se escriben en C y se pueden compilar y se cargan dinámicamente indicandolo en el fichero de configuración de PHP (php.ini) mediante la directiva extension.

En windows:

extension=php_curl.dll

En unix:

extension=php_curl.so

En este enlace se pueden ver todas las funciones de PHP categorizadas por tipos de extensiones:

9 Acceso al sistema de archivos

http://php.net/manual/en/book.filesystem.php

Esta extensión forma parte del core de PHP y no hace falta hacer nada especial para usarla.

En PHP los ficheros son tratados como streams de datos:

Un stream es un recurso que exhibe un comportamiento de transmisión. Es decir, puede leerse o escribirse de forma lineal, y se puede acceder a una ubicación arbitraria dentro del stream. Sirven para modelar ficheros, sockets, compresión de datos, etcétera.

Estas son las funciones que PHP ofrece para tratar con ficheros:

Vamos a ver las fundamentales para leer y escribir en archivos.

9.1 Abrir un fichero

http://php.net/manual/en/function.fopen.php

$fp = fopen("c:\\folder\\resource.txt", "r");

También podemos abrir URLs como si de ficheros se tratasen:

$fp = fopen('http://google.es', 'r');

9.2 Leer en un fichero

http://php.net/manual/en/function.fread.php

Se hace a través de un recurso el cual es devuelto por fopen.

$filename = "c:\\folder\\resource.txt";
$fp = fopen($filename, "r");
$contents = fread($fp, filesize($filename));

El código anterior leería el fichero completo.

9.3 Escribir en un fichero

http://php.net/manual/en/function.fwrite.php

Se hace a través de un recurso el cual es devuelto por fopen.

$fp = fopen('c:\\folder\\resource.txt', 'w');
fwrite($fp, 'Hola que tal');

9.4 Cerrar un fichero

http://php.net/manual/en/function.fclose.php

Siempre debemos cerrar los ficheros que hemos abierto.En los ejemplos de lectura y escritura anteriores, una vez que se han realizado las operaciones habría que usar fclose.

fclose($fp);

PHP ofrece funciones para leer y escribir sobre fichero de más alto nivel que resultan muy útiles por su sencillez e inmediatez.

9.5 Leer un fichero completo y asignar su contenido a una string

http://php.net/manual/en/function.file-get-contents.php

$homepage = file_get_contents('http://www.example.com/');

9.6 Escribir una string a un fichero

http://php.net/manual/en/function.file-put-contents.php

$current = file_get_contents($file);
// Append a new person to the file
$current .=
"John Smith\n";
// Write the contents back to the file
file_put_contents($file, $current);

9.7 Leer un fichero completo y asignarlo a un array

http://php.net/manual/en/function.file.php

Cada línea del fichero la añade a un elemento de un array.

$lines = file('http://www.example.com/');

foreach ($lines as $line_num => $line) {
   
echo "Line #<b>{$line_num}</b> : " . htmlspecialchars($line) . "<br />\n";
}

9.8 Leer un fichero y volcarlo a la salida estándar

http://php.net/manual/en/function.readfile.php

$file = 'monkey.gif';
readfile($file);

10 Acceso a base de datos con PDO

http://php.net/manual/en/book.pdo.php

PHP ofrece varias extensiones para acceder a bases de datos. La más usada con diferencia es PDO (PHP Data Object)  que proporciona una interfaz común para realizar las mismas operaciones sobre distintas bases de datos.

Cada base de datos accesible con PDO debe proporcionar sus propios drivers PDO, los cuales son cargados como extensiones de PHP y deben ser, por tanto, añadidos al fichero de configuración php.ini.

PDO ofrece una capa de  abstracción  a nivel de acceso a datos no a nivel de base de datos. Esto significa que, aunque la interfaz de acceso a datos es la misma para todos las bases de datos soportadas, las sentencias SQL hay que proporcionarlas siguiendo la sintaxis de cada base de datos. Esto es, no proporciona un SQL común y reescribe las sentencias SQL para cada base de datos.

Existen otras librerías que sí proporcionan una capa de abstracción completa, como por ejemplo Doctrine DBAL.

Actualmente existen drivers PDO para las siguientes bases de datos:

10.1 Conexiones con PDO

Sea cual sea la base de datos a la que vayamos a conectarnos con PDO, la conexión se realiza creando instancias del objeto PDO:

$dbh = new PDO('mysql:host=localhost;dbname=test', 'userdb', 'passdb'));

El primer argumento es lo que se llama la cadena de conexión (DSN), que depende del tipo de base de datos que usemos. Los siguientes son el username, password y un array de opciones.

Si la conexión falla se lanzará una excepción con lo que podremos controlar el error.

try {
   $dbh =
new PDO('mysql:host=localhost;dbname=test', $user, $pass);
   
foreach($dbh->query('SELECT * from FOO') as $row) {
       print_r($row);
   }
   $dbh =
null;
}
catch (PDOException $e) {
   
print "Error!: " . $e->getMessage() . "<br/>";
   
die();
}

La conexión se mantendrá abierta mientras el objeto PDO exista. En el momento en que no haya más referencias a dicho objeto, la conexión se cierra. Podemos eliminar todas las referencias a dicho objeto simplemente asignandolo a NULL.

$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
// use the connection here
$sth = $dbh->query(
'SELECT * FROM foo');

// and now we're done; close it
$sth =
null;
$dbh =
null;

Si el script termina, por defecto también se cierra la conexión. Pero si en la creación del objeto PDO se indicó conexión persistente, la conexión no finaliza al terminar el script y se cachea y queda disponible para que otro script la utilice. Esto puede ser útil en aplicaciones web con muchas peticiones para que no haya que andar creando y cerrando conexiones en cada petición.

$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(
   PDO::ATTR_PERSISTENT =>
true
));

10.2 Sentencias preparadas

Las sentencias preparadas son soportadas por algunas bases de datos. La idea consiste en construir una plantilla parametrizada con la secuencia. Dicha plantilla se analiza / compila y optimiza una sola vez y se puede reusar con distintos parámetros todas las veces que se desee sin tener que repetir el ciclo análisis / compilación / optimización. Esto da lugar a un incremento en la velocidad con que se realizan las consultas.

Otra ventaja de usar las sentencias preparadas es que el propio driver que sustituye los parámetros por los valores finales y se encarga de añadir las comillas. Esto protege a las consultas de la “SQL injection”.

Con parámetros con nombre.

// $dbh es un objeto PDO

$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(
':name', $name);
$stmt->bindParam(
':value', $value);
// insert one row
$name =
'one';
$value =
1;
$stmt->execute();

// insert another row with different values
$name =
'two';
$value =
2;
$stmt->execute();

Con parámetros posicionales.

// $dbh es un objeto PDO

$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
$stmt->bindParam(
1, $name);
$stmt->bindParam(
2, $value);

// insert one row
$name =
'one';
$value =
1;
$stmt->execute();

// insert another row with different values
$name =
'two';
$value =
2;
$stmt->execute();

Con PDO haremos siempre las consultas de esta manera. Esto es:

El método execute() devuelve un boolean con el resultado de la operación, True si ha ido bien y False si va mal.

Con los métodos errorCode() y errorInfo() del objeto PDOStatement podemos ver más información sobre el error que ocurre cuando la consulta ha ido mal.

Si la consulta es de selección habrá que recoger el resultado. Esto se hace con los métodos

fetch() 

$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
if ($stmt->execute('juanda')) {
 
while ($row = $stmt->fetch()) {
   print_r($row);
 }
}

y fetchAll().

$sth = $dbh->prepare("SELECT name, colour FROM fruit");
$sth->execute();

/* Fetch all of the remaining rows in the result set */
print("Fetch all of the remaining rows in the result set:\n");
$result = $sth->fetchAll();
print_r($result);

10.3 Transacciones

Para las bases de datos que admiten transacciones, PDO ofrece la siguiente manera de realizarlas.

try {  
 $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

 $dbh->beginTransaction();
 $dbh->exec(
"insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')");
 $dbh->exec(
"insert into salarychange (id, amount, changedate)
     values (23, 50000, NOW())"
);
 $dbh->commit();
 
}
catch (Exception $e) {
 $dbh->rollBack();
 
echo "Failed: " . $e->getMessage();
}

Si por lo que sea se produce un error en un script y se aborta la ejecución de este, las sentencias SQL que no hayan sido confirmadas con commit(), serán revertidas automáticamente.

10.4 Conexiones persistentes a base de datos

http://php.net/manual/en/features.persistent-connections.php

La conexión persistente no se cierra cuando acaba el script. De manera que cuando se ejecuta de nuevo un script sobre el mismo proceso PHP que usa una misma base de datos, con un mismo usuario, puede volver a reutilizar la conexión que abrió anteriormente.

Cada proceso PHP crea y mantiene su propia conexión persistente. Por ello puede ser problemático usar este tipo de conexión cuando el nº de procesos hijos sea muy alto; se puede llegar a superar el máximo nº de conexiones que el servidor de base de datos puede soportar.

En ningún caso las conexiones persistente son un pool de conexiones. En PHP no existe el concepto de pool de conexiones.

$mbd = new PDO('mysql:host=localhost;dbname=prueba', $usuario, $contraseña, array(
   PDO::ATTR_PERSISTENT =>
true
));

Nota: El driver PDO_SQLSRV no soporta conexiones persistentes.

10.5 Conexión a bases de datos MSSQL Server

Podemos conectar a bases de datos de Microsoft con los drivers:

PDO_DBLIB no está disponible para la plataforma Windows desde PHP 5.3. Así que solo nos queda PDO_SQLSRV para poder acceder a bases de datos MSSQL Server desde Windows.

PDO_SQLSRV es un desarrollo de Microsoft y puede descargarse desde:

La instalación y carga en Windows se explica en 2 Instalación y configuración de PHP en windows.

Veamos un ejemplo de conexión a un servidor MSSQL server.

<?php
$dsn =
'sqlsrv:server=82.196.6.32;Database=prueba';
$user =
'kokolo';
$password =
'Pa$$prueba';

try
{
  $pdo_object =
new \PDO($dsn, $user, $password);
}
catch (PDOException $e)
{
 
echo 'Connection failed: ' . $e->getMessage() . PHP_EOL;
 
exit();
}

$sql =
"SELECT * from productos";
$pdo_statement_object = $pdo_object->prepare($sql);
$pdo_statement_object->execute();
$result = $pdo_statement_object->fetchAll();
print_r($result);

la única particularidad es la forma de la cadena de conexión (DSN), propia del driver PDO_SQLSRV. A partir de ese momento se utiliza la interfaz que proporciona PDO sin más, pues para eso es una abstracción de acceso a datos. Por otro lado las consultas SQL se realizan con el dialecto SQL de MSSQL.

11 Ejercicio 3. Gestor de claves.

En este ejercicio construirás una aplicación CLI mediante la cual el usuario podrá gestionar los datos de cuentas y servicios que utiliza. Cada cuenta se corresponderá con una entrada con los siguientes datos:

La aplicación permitirá :

Los password se almacenarán encriptados. Para lo cual el usuario tendrá que asignar una clave a su almacén de cuentas/servicios.

Se implementarán dos tipos de  almacén de datos:

Por configuración se elegirá cual de los dos almacenes se utilizará.

Cada registro se encapsulará en una clase denominada

Los almacenes de datos se modelarán con clases que implementen la siguiente interfaz:

namespace Acme\KeyStorage;

use Acme\KeyStorage\KeyRegister;
use Acme\TopSecret\Crypter;

Interface KeyStorageInterface{

 
public function __construct(Crypter $crypter, ...$options);
 
 
public function add(KeyRegister $keyregister): bool;
 
public function save(): bool;
 
public function find(string $name);
 
public function getAll(): array;
 
public function delete(string $name): bool;      
}

12 PHP en la web

Desde sus inicios PHP ha estado orientado hacia la programación de aplicaciones web. Aunque con el tiempo ha ido haciéndose cada vez más un lenguaje de propósito general,  sigue siendo su faceta web la que sigue prevaleciendo sobre cualquier otra.

PHP puede utilizarse con cualquier servidor web que soporte CGI (prácticamente todos) o FastCGI. Esta última forma de conectar el servidor web con la aplicación externa PHP es más adecuada y eficiente y, de estar disponible, debe usarse  preferentemente sobre CGI. De hecho CGI es considerado una tecnología obsoleta que se mantiene únicamente en sistemas heredados.

La diferencia fundamental entre CGI y FastCGI es que en el primero el servidor web invoca a la aplicación CGI pasándole la request debidamente encapsulada según la especificación, recoge la salida estandard de la aplicación y devuelve la respuesta. Por tanto cada petición debe crear un nuevo proceso CGI. En el caso de FastCGI la comunicación entre la aplicación PHP y el servidor web se produce a través de sockets (unix o TCP, ambos pueden utilizarse). La aplicación FastCGI es, en realidad, un servidor que crea varios procesos capaces de ejecutar código PHP, de esta manera no es necesario crear un nuevo proceso en cada petición, si no que el servidor web envía las peticiones al servidor FastCGI a través de un socket. FastCGI aumenta considerablemente el rendimiento y las escalabilidad.

Así pues, a través de FastCGI se puede montar un servidor de aplicaciones PHP con cualquiera de los servidores web más populares:

No obstante, si se utiliza apache, existe una forma más de ejecutar aplicaciones PHP. Se trata de instalar PHP como módulo de apache. En este caso, cada proceso que apache crea para resolver las peticiones tiene incrustado un intérprete de PHP, con lo que no es necesario ni crear un nuevo proceso ni utilizar otros procesos externos PHP al estilo de FastCGI. Esta es la manera tradicional en que se ha instalado PHP a lo largo y ancho del planeta. Es muy sencilla de implementar y resulta muy eficaz. El problema es que los procesos de apache, al llevar un intérprete incrustado, ocupan más memorias y cuando el nº de procesos crece al aumentar el nº de peticiones, la memoria del servidor se puede ver exhausta.

Sea como sea, cuando se usa PHP en la web, la petición HTTP que llega al servidor web y las características del servidor son encapsuladas en las variables superglobales:

Todas estas variables son arrays asociativos y están disponible en cualquier script PHP que esté tras un servidor web para que la usemos en nuestros desarrollos.

12.1 Request

En una aplicación web, los datos llegan a través de la petición (request). Dichos datos pueden venir de formularios HTML (POST), por parámetros que se pasan en la URL (GET) o por cualquier otra operación (verbo) HTTP como puede ser PUT, DELETE, HEAD. Estos últimos son utilizados casi en exclusiva por los servicios web de tipo RESTful.

Obtenemos las variables que vienen en la petición con la superglobal $_REQUEST. Si sabemos que la petición ha sido realizada con la operación GET, podemos usar $_GET, y si sabemos que ha sido realizada por POST podemos usar $_POST. Las cookies también forman parte de la petición y podemos obtenerlas mediante la superglobal $_COOKIE. En todo caso $_REQUEST contiene la información de $_GET, $_POST y $_COOKIE simultaneamente, aunque todas ellas son variables independientes.

Es muy importante que validemos, saneemos y escapemos todos los datos que usemos de estas variables, ya que son la puerta de entrada de casi todos los problemas de seguridad. Hay que tener en cuenta que los clientes pueden enviarnos lo que les dé la gana. Contemos o no con medidas perimétricas de seguridad, como cortafuegos y sistemas de intrusión, es vital que el desarrollador tenga en cuenta este hecho y ponga los medios para paliar la entrada de código malicioso en su aplicación.

Para filtrar los datos de la request y evitar amenazas debemos usar filtros de datos:

Ejemplos:

$ip_a = '127.0.0.1';
$ip_b =
'42.42';

if (filter_var($ip_a, FILTER_VALIDATE_IP)) {
   
echo "Esta dirección IP (ip_a) es válida.";
}
if (filter_var($ip_b, FILTER_VALIDATE_IP)) {
   
echo "Esta dirección IP (ip_b) es válida.";
}

Usar htmlentities para escapar el código HTML:

$str = "A 'quote' is <b>bold</b>";

// Produce: A 'quote' is &lt;b&gt;bold&lt;/b&gt;
echo htmlentities($str);

// Produce: A &#039;quote&#039; is &lt;b&gt;bold&lt;/b&gt;
echo htmlentities($str, ENT_QUOTES);

Realizar un filtrado y saneamiento adecuado y seguro de la petición no es una tarea inmediata. Lo mejor es utilizar un framework web que haga el trabajo en condiciones y nos libere de esa tarea entregándonos un objeto request adecuado. Aún así siempre habrá que validar los datos que llegan en la petición.

12.2 Response

La respuesta HTTP consta de un código de estado, una o varias líneas de cabeceras una línea vacía y opcionalmente un mensaje (body, el cuerpo de la respuesta).

Para conseguir una respuesta en PHP cuando estamos en un entorno web basta con enviar a la salida estándar una cadena o el código HTML resultante de interpretar un script donde se mezcla HTML y PHP. Eso dará lugar al cuerpo que usará el servidor web en la respuesta.

El código de estado será 200 si se ha conseguido interpretar bien el script PHP o 500 si ha ocurrido algún problema. Y como cabeceras se enviarán las que el servidor web tenga establecidas.

Si queremos cambiar alguna cabecera desde el script PHP podemos utilizar la función header().

Es importante tener en cuenta que header() debe ser llamado antes de mostrar nada por pantalla, etiquetas HTML, líneas en blanco desde un fichero o desde PHP. Es un error muy común leer código con funciones como include o require, u otro tipo de funciones de acceso de ficheros que incluyen espacios o líneas en blanco que se muestran antes de llamar a la función header().

Ejemplos:

<!DOCTYPE html>
<html>
  <body>

      <h1>Hoy es:
<?php echo date("Y-m-d H:i:s") ?></h1>

      El contenido de la request es:
      <pre>
         
<?php print_r($_REQUEST)?>
      </pre>

  </body>
</html>

<?php
// Vamos a mostrar un PDF
header(
'Content-type: application/pdf');

// Se llamará downloaded.pdf
header(
'Content-Disposition: attachment; filename="downloaded.pdf"');

// La fuente de PDF se encuentra en original.pdf
readfile(
'original.pdf');
?>

<?php
/* Redirecciona a una página diferente en el mismo directorio el cual se hizo la petición */
$host  = $_SERVER[
'HTTP_HOST'];
$uri   = rtrim(dirname($_SERVER[
'PHP_SELF']), '/\\');
$extra =
'mypage.php';
header(
"Location: http://$host$uri/$extra");
exit;
?>

Lo ideal, de nuevo, es utilizar un framework que nos permita construir tanto el cuerpo de la respuesta fácilmente mediante un sistema de plantilla como el envío de headers y códigos de respuesta.

12.3 Cookies

Las cookies proporcionan un mecanismo que permite a las aplicaciones web identificar al cliente que hace la petición.

El funcionamiento es sencillo; la aplicación envía al cliente mediante la cabecera de respuesta Set-Cookie un nombre y un valor de la cookie e información sobre el tiempo que debe perdurar en el navegador. El navegador la crea asociada al dominio desde donde se solicitó la creación de la cookie y a partir de ese momento la envía, mientras dure su validez, en todas las peticiones utilizando la cabecera Cookie de la petición. Cuando PHP la recoge, la deserializa y almacena el resultado en la superglobal $_COOKIE.

Setting new cookie
=============================
<?php
setcookie(
"name","value",time()+$int);
/*name is your cookie's name
value is cookie's value
$int is time of cookie expires*/

?>

Getting Cookie
=============================
<?php
echo $_COOKIE["your cookie name"];
?>

Updating Cookie
=============================
<?php
setcookie(
"color","red");
echo $_COOKIE["color"];
/*color is red*/
/* your codes and functions*/
setcookie(
"color","blue");
echo $_COOKIE["color"];
/*new color is blue*/
?>

Deleting Cookie
==============================
<?php
unset($_COOKIE["yourcookie"]);
/*Or*/
setcookie(
"yourcookie","yourvalue",time()-1);
/*it expired so it's deleted*/
?>

12.4 Session

El mecanismo de sesión en aplicaciones web permite mantener en el servidor el estado de la aplicación a pesar de que HTTP es un protocolo sin estado. El truco para conseguir esto es crear una cookie en el navegador, denominada cookie de sesión, la cual es enviada en cada petición y que se corresponde con un fichero creado en el servidor en el cual se almacenan los datos que la aplicación necesita entre peticiones para mantener su estado.

En cada petición el intérprete de PHP recoge la cookie de sesión, abre el archivo correspondiente (si no lo tiene cacheado), lo deserializa y crea la superglobal $_SESSION con el resultado.

Cuando se añade una nueva variable a la sesión, es decir a la superglobal $_SESSION, antes de finalizar el script y enviar el resultado de vuelta, se vuelve a serializar el contenido y se almacena en su archivo correspondiente, de manera que en la próxima petición  que venga con la cookie de sesión en cuestión se vuelva a deserializar y crear la variable $_SESSION tal y como estaba en la última respuesta.

Para crear una sesión o reanudar la anterior se utiliza la función session_start().

Ejemplo:

pagina1.php

<?php
// pagina1.php

session_start();

echo 'Bienvenido a la página #1';

$_SESSION[
'color']  = 'verde';
$_SESSION[
'animal'] = 'gato';
$_SESSION[
'instante']   = time();

// Funciona si la cookie de sesión fue aceptada
echo '<br /><a href="pagina2.php">página 2</a>';

// O quizás pasar el id de sesión, si fuera necesario
echo '<br /><a href="pagina2.php?' . SID . '">página 2</a>';
?>

Ahora el mismo cliente hace una petición a la pagina2.php y puede obtener los valores que se almacenaron en la sesión en la petición anterior a la pagina1.php

pagina2.php

<?php
// pagina2.php

session_start();

echo 'Bienvenido a la página #2<br />';

echo $_SESSION['color'];  // verde
echo $_SESSION['animal']; // gato
echo date('Y m d H:i:s', $_SESSION['instante']);

// Puede ser conveniente usar el SID aquí, como hicimos en pagina1.php
echo '<br /><a href="pagina1.php">página 1</a>';
?>

12.5 Subida de archivos

La subida de archivos desde un formulario HTML se realiza mediante un formulario multipart:

<form enctype="multipart/form-data" action="/upload.php" method="POST">
   <!-- MAX_FILE_SIZE must precede the file input field -->
   <input type=
"hidden" name="MAX_FILE_SIZE" value="30000" />
   <!-- Name of input element determines name in $_FILES
array -->
   Send this file: <input name=
"userfile" type="file" />
   <input type=
"submit" value="Send File" />
</form>

Cuando se envía un formulario multipart con un fichero adjunto, PHP rellena la variable superglobal $_FILES con la  siguiente información.

$_FILES['userfile']['name']

El nombre original del fichero en la máquina cliente.

$_FILES['userfile']['type']

El tipo de mime del archivo, si el navegador proporcionó esta información. Un ejemplo sería "image / gif". Sin embargo, este tipo de mime no está verificado en el lado de PHP y, por lo tanto, no da su valor por sentado.

$_FILES['userfile']['size']

El tamaño en bytes del fichero subido.

$_FILES['userfile']['tmp_name']

El nombre del fichero temporal donde se ha almacenado provisionalmente en el servidor.

$_FILES['userfile']['error']

El error producido, si lo hubiera.

El siguiente script muestra como se recogería el fichero enviado por el formulario anterior usando la superglobal $_FILES.

<?php

$uploaddir =
'c:\\uploads';
$uploadfile = $uploaddir . basename($_FILES[
'userfile']['name']);

echo '<pre>';
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
   
echo "File is valid, and was successfully uploaded.\n";
}
else {
   
echo "Possible file upload attack!\n";
}

echo 'Here is some more debugging info:';
print_r($_FILES);

print "</pre>";

?>

También se pueden subir varios fichero a la vez. El formulario sería algo así:

<form action="" method="post" enctype="multipart/form-data">
<p>Pictures:
<input type=
"file" name="pictures[]" />
<input type=
"file" name="pictures[]" />
<input type=
"file" name="pictures[]" />
<input type=
"submit" value="Send" />
</p>
</form>

Y el script que recoge los ficheros:

<?php
foreach ($_FILES["pictures"]["error"] as $key => $error) {
   
if ($error == UPLOAD_ERR_OK) {
       $tmp_name = $_FILES[
"pictures"]["tmp_name"][$key];
       
// basename() may prevent filesystem traversal attacks;
       
// further validation/sanitation of the filename may be appropriate
       $name = basename($_FILES[
"pictures"]["name"][$key]);
       move_uploaded_file($tmp_name,
"data/$name");
   }
}

13 Ecosistema PHP

En esta última sección veremos estándares y herramientas que nos ayudarán a utilizar PHP de una manera profesional, y nos permitirán construir aplicaciones modulares, escalables y mantenibles.

13.1 PHP Framework Interop Group y las PSR

https://www.php-fig.org/

El “PHP Framework Interop Group”, es un grupos de programadores involucrados en el desarrollo de algunas de las aplicaciones PHP más conocidas, establecidas y de mayor impacto, entre las que se encuentran:

Y muchas más.

El objetivo de este grupo es proponer maneras estándar de resolver problemas comunes en cualquier desarrollo, pensando en las buenas prácticas, la modularidad, el mantenimiento y la reutilización de código.

Las  propuestas se denominan PSR-N (PHP Standar Recommendatios), donde “N” es el nº que identifica a la propuesta. Actualmente existen las siguientes:

https://www.php-fig.org/psr/

PSR-1

Basic Coding Standard

PSR-2

Coding Style Guide

PSR-3

Logger Interface

PSR-4

Autoloading Standard

PSR-6

Caching Interface

PSR-7

HTTP Message Interface

PSR-11

Container Interface

PSR-13

Hypermedia Links

PSR-15

HTTP Handlers

PSR-16

Simple Cache

Los números que faltan se corresponden con propuestas que se están obsoletas, se han abandonado o están en revisión.

La lectura y aplicación de estas propuesta es muy  recomendable si queremos avanzar rápido en el dominio de PHP y programar adecuadamente evitando muchos fallos que ya están muy superados. Se trata de aprovechar este trabajo para aplicar  la acertada idea de “no reinventes la rueda”.

13.2 Composer

https://getcomposer.org/

https://github.com/juanda/juandaBlog/blob/master/source/tutoriales/tutorial-de-composer.md

Composer es una herramienta de gestión de librerías de terceros (componentes). Es similar a maven (java), pip (python), npm (nodejs/javascript) o bower (javascript). Se trata de una herramienta imprescindible en el toolbox de cualquier desarrollador PHP.

La idea es definir en un archivo JSON las dependencias que el proyecto va a utilizar, con las versiones, o rango de versiones de dichas dependencias. La herramienta resuelve, si es posible, la combinación óptima de versiones de todas las dependencias que satisfacen lo indicado en el archivo JSON, las instala en el proyecto y crea un autoloading que sigue la recomendación PSR-4 para que la carga de las clases de cada librería sea automática.

13.3 Testing con PHPUnit

Cualquier aplicación, especialmente si se hacen muchos lanzamientos (release) en poco tiempo, está sujeta a que al corregir errores o añadir nuevas funcionalidades se generen nuevos errores o que cosas que funcionaban dejen de hacerlo.

Para reducir y aliviar esta pesada carga con la que todos los desarrolladores debemos convivir nada mejor que incorporar a nuestros proyectos una buena batería de tests que irá creciendo conforme el proyecto lo haga.

Cada vez que toquemos algo, ya sea para corregir, ya sea para mejorar, ejecutaremos la batería de test para saber, al menos, que lo que funcionaba antes sigue haciéndolo tras los cambios. Además, cuando añadamos nuevas funcionalidades o pensemos que hay un caso que deberíamos probar, añadiremos nuevos tests que ampliarán la cobertura de test sobre todos los casos de uso posible, y proporcionará robustez, seguridad y confianza en el desarrollo.

En el mundo PHP, PHPUnit es el testing framework por excelencia:

Es importante saber que para someter a nuestras aplicaciones a baterías de tests significativos no basta contar con una buena herramienta como PHPUnit. Esto, que puede parecer una mala noticia, por el contrario es una muy buena cosa. Ya que la otra pata que falta para poder hacer tests sobre nuestras aplicaciones es que estas estén debidamente diseñadas para ello. Y esto significa construir un código donde los objetos y componentes estén suficientemente desacoplados. Lo cual no solo permite realizar los tests en condiciones, si no que redunda en la modularidad, mantenibilidad y escalabilidad de la aplicación.

La instalación de PHPUnit, como librería de tercero que es, puede y debe hacerse con composer. La siguiente URL es un buen punto de partida para utilizar PHPUnit:

13.4 Documentando con PHPDocumentor

Otra tarea importante que debe realizarse con dedicación y esmero en cualquier proyecto de desarrollo que se precie es la documentación del mismo. La documentación se debe hacer a varios niveles:

PHPDocumentor es una herramienta que nos ayudará a construir hermosas páginas web con la documentación del código, extraída de los comentarios de este.

La herramienta trabaja en base a los denominados bloques de documentación donde podemos usar anotaciones especiales (los items que comienzan con “@”) para indicar elementos como los parámetros de una función o lo que devuelve.

<?php
/**
 * A summary informing the user what the associated element does.
 *
 * A *description*, that can span multiple lines, to go _in-depth_ into

  * the details of this element
 * and to provide some background information or textual references.
 *
 * @param string $myArgument With a *description* of this argument, these may also
 *    span multiple lines.
 *
 * @return void
 */

 
function myFunction($myArgument)
 {
 }

También puede instalarse con composer.

13.5 Frameworks web

https://openwebinars.net/blog/los-10-mejores-frameworks-php-que-solicitan-las-empresas

Si vamos a emprender el desarrollo de una aplicación web digna con PHP, sin lugar a dudas tenemos que utilizar y aprender en profundidad alguno de los excelentes frameworks que existen actualmente en la escena PHP.

14 Ejercicio 4. Mejora del gestor de claves

Rehacer la aplicación desarrollada en 11 Ejercicio. Gestor de claves. para utilizar el componente console del proyecto symfony. Utilizar composer para su incorporación al proyecto.

15 Ejercicio 5. Gestor de clave web

Convertir la aplicación desarrollada en 11 Ejercicio. Gestor de claves. en una aplicación web. No es necesario que se haga un control de usuarios.

16 Ejercicio final 6. Construcción de un framework web

17 Más cosas

17.1 Depurar con XDebug

Bajar la librería dinámica xdebug de:

Colocarla en C:\php\ext

Y añadir al php.ini:

zend_extension=php_xdebug-2.6.0-7.0-vc14-nts-x86_64.dll

xdebug.remote_autostart=
1
xdebug.remote_enable=
1
xdebug.remote_host=localhost
xdebug.remote_port=
9000
xdebug.remote_handler=dbgp

17.2 PHP Thread Safe vs. Non Thread Safe

https://stackoverflow.com/questions/1623914/what-is-thread-safe-or-non-thread-safe-in-php

https://stackoverflow.com/questions/681081/is-php-thread-safe

http://neosmart.net/blog/2008/dont-believe-the-lies-php-isnt-thread-safe-yet/

Con IIS Microsoft dice explícitamente que se use la versión NTS:

https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/hh994592(v=ws.11)#to-download-and-install-php-and-wincache

PHP no termina de ser totalmente Thread Safe, aunque se use la compilación TS. Por ello es recomendable usar la versión NTS. Eso significa que al acoplarlo a un web server hay que usar alguna forma que no requiera Thread Safety. Por ejemplo CGI o FastCGI.

Internet Information Service proporciona una manera de comunicarse con aplicaciones muy eficiente a través de lo que se llama ISAPI (Internet Server Application Interface), pero requiere que las aplicaciones sean Thread Safe pues esta manera de trabajar se basa en la creación de hilos. Como no podemos confiar en PHP TS, pues descartamos esta posibilidad y usamos PHP NTS con FastCGI como método óptimo de uso de PHP con IIS.

17.3 Máquina virtual de PHP7

https://nikic.github.io/2017/04/14/PHP-7-Virtual-machine.html

17.4 PHP por dentro

http://www.phpinternalsbook.com/

17.5 Soluciones a los ejercicios

En el siguiente repositorio de github puedes encontrar una posible solución a los ejercicios propuestos: