La Comunidad PHP sigue incorporando nuevas características y funcionalidades a este popular lenguaje de programación con el objetivo de hacerlo más atractivo, funcional y útil. La versión 7 (liberada el pasado 3 de diciembre) es considerada una de las mayores actualizaciones en toda la historia de PHP ya que el motor (Zend Engine) fue refactorizado con el objetivo de lograr mejor rendimiento y mejor uso de la memoria. También se introdujeron nuevas características que facilitan y aceleran el desarrollo web.
1. Rendimiento mejorado
Gracias a la version 3.0 del motor Zend, PHP 7 es 2 veces más rápido y consume 50% menos memoria que PHP 5.6. Para ver gráficos de comparación consulte
- Speeding up the Web with PHP 7
- PHP 7 – Performance
- The Definitive PHP 5.6, 7.0, 7.1, 7.2, 7.3, and 7.4 Benchmarks (2020)
2. Nuevos tipos de variables como argumentos
PHP está convirtiéndose en un lenguaje mixto o sea dinámico y tipeado ejemplo de esto es que ahora se aceptan nuevos tipos de variables como argumentos de funciones. Si el valor dado es de un tipo incorrecto se lanzará una excepción TypeError. Antes de PHP 7 podíamos utilizar nombre de clases o interfaz, self, array, callable ahora PHP7 añade los siguientes tipos: int, float, bool y string.
El valor de la directiva strict_types afecta el comportamiento de las declaraciones de tipo ya sea de argumentos o de retorno, en el modo por defecto PHP convertirá automaticamente de un tipo a otro. En el modo estricto el valor devuelto debe ser del tipo correcto.
Modo débil (por defecto)
<?php
function test_param(int $a, bool $is_ready, string $str) {
echo $a, ' => ', gettype($a), "\n";
echo $is_ready, ' => ', gettype($is_ready), "\n";
echo $str, ' => ', gettype($str), "\n";
}
class Test {
public function __toString() {
return Test::class;
}
}
test_param(5.5, true, new Test());
// Print
// 5 => integer
// 1 => boolean
// Test => string
Modo estricto
<?php
declare(strict_types = 1);
function test_param(int $a, bool $is_ready, string $str) {
echo $a, ' => ', gettype($a), "\n";
echo $is_ready, ' => ', gettype($is_ready), "\n";
echo $str, ' => ', gettype($str), "\n";
}
class Test {
public function __toString() {
return Test::class;
}
}
test_param(5, true, new Test());
// Print
// Fatal error: Uncaught TypeError: Argument 3 passed to test_param()
// must be of the type string, object given...
3. Declaraciones tipo de retorno
Ahora a las funciones se le puede especificar un tipo de retorno de forma similar a las declaraciones de tipo de argumento.
Modo débil
<?php
function sum($a, $b): int {
return $a + $b;
}
function sum_f($a, $b): float {
return $a + $b;
}
class Test {
public function __toString() {
return __CLASS__;
}
}
function return_str(): string {
return (new Test());
}
// Print 5
echo sum(3.5, 2), PHP_EOL;
// Print 5.5
echo sum_f(3.5, 2), PHP_EOL;
// Print Test
echo return_str(), PHP_EOL;
Modo estricto
<?php
declare(strict_types=1);
function sum($a, $b): int {
return $a + $b;
}
function sum_f($a, $b): float {
return $a + $b;
}
class Test {
public function __toString() {
return __CLASS__;
}
}
function return_str(): string {
return (new Test());
}
// Print
// Fatal error: Uncaught TypeError: Return value of sum() must be of the type
// integer, float returned...
echo sum(3.5, 2), PHP_EOL;
// Print 5.5
echo sum_f(3.5, 2), PHP_EOL;
// Print
// Fatal error: Uncaught TypeError: Return value of return_str() must be of the
// type string, object returned...
echo return_str(), PHP_EOL;
4. Operador de fusión de nulo
El operador de fusión de nulo (??) se ha añadido como un atajo al operador ternario cuando este último es usado con isset. Devuelve el primer operando si existe y no es nulo en caso contrario devuelve el segundo operando.
<?php
// Fetches the value of $_GET['user'] and returns 'nobody' if it does not exist.
$username = $_GET['user'] ?? 'nobody';
// This is equivalent to:
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
Puede concatenarse varios ?? devolviendo el primer valor definido y que no es nulo.
<?php
$username = $_GET['user'] ?? $_POST['user'] ?? 'nobody';
El operador ?? tiene un significado similar al operador || usado en otros lenguages de programación como Javascript, Perl o Bash, pero téngase en cuenta que el operador || además comprueba si el valor es falso mientras que ?? solo comprueba si el valor existe y no es nulo.
#!/bin/bash
# Print yes
perl -e '$a = $b || "yes"; print($a);'
# Print yes
php -r '$a = $b ?? "yes"; print_r($a);'
# Print yes
perl -e '$a = 0 || "yes"; print($a);'
# Print 0
php -r '$a = 0 ?? "yes"; print_r($a);'
# Print I have a value
perl -e '$a = "I have a value" || "yes"; print($a);'
# Print I have a value
php -r '$a = "I have a value" ?? "yes"; print_r($a);'
5. Operador nave espacial
El operador nave espacial se utiliza para comparar dos expresiones. Si comparamos $a con $b, entonces devuelve -1 si $a < $b, 0 si $a = $b y 1 si $a > $b.
<?php
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1
6. Asignar un arreglo a una constante
A partir de PHP 7 se puede asignar un arreglo a una constante definida usando la sentencia define.
<?php
define('DAYS', [
'sun' => 'Sunday',
'mon' => 'Monday',
'tue' => 'Tuesday',
'wed' => 'Wednesday',
'thu' => 'Thursday',
'fri' => 'Friday',
'sat' => 'Saturday'
]);
// PrSunday
echo DAYS['sun'], PHP_EOL;
7. Clases anónimas
PHP 5.3 introdujo el concepto de funciones anónimas ahora PHP 7 introduce el concepto de clases anónimas y como su nombre lo indica es una clase que no tiene nombre. Una clase anónima realizará una función especifíca y nunca debe violar el principio de sola responsabilidad. Puede usar una clase anónima si:
- Su clase no necesita ser documentada
- Su clase tiene poco métodos y propiedades
- Solo se necesita una instancia durante la ejecución de su aplicación
- Se usa inmediatamente después de su definición
- Necesita crear objetos al vuelo
- El nombre no añade claridad y legibilidad al código fuente
- Desea añadir o modificar un comportamiendo de una clase existente
- Desea evitar el impacto de cargar la definición de clase desde un fichero externo (disco duro)
Desde mi punto de vista una clase anónima puede ser muy útil a la hora de devolver valores múltiples en una función de retorno y tiene un uso similar al objecto {} en Javascript.
<?php
define('PLAYERS', [
'1' => ['Garry Kasparov', 2851],
'2' => ['Anatoly Karpov', 2780],
'3' => ['Magnus Carlsen', 2882],
'4' => ['Bobby Fischer', 2785]
]);
interface PlayerDetailsInterface
{
public function getName();
public function getRanking();
}
/**
* Get Chess player detail
*
* @return PlayerDetailsInterface
*/
function getChessPlayerDetails($id): PlayerDetailsInterface
{
// Do some processing, consult some internet DB, Webservice or another resource
$player = PLAYERS[$id];
$name = $player[0];
$ranking = $player[1];
return new class($name, $ranking) implements PlayerDetailsInterface
{
private $name;
private $ranking;
public function __construct($name, $ranking)
{
$this->name = $name;
$this->ranking = $ranking;
}
public function getName()
{
return $this->name;
}
public function getRanking()
{
return $this->ranking;
}
};
}
foreach (array_keys(PLAYERS) as $id) {
/**@var PlayerDetailsInterface $detail */
$detail = getChessPlayerDetails($id);
echo $detail->getName(), ': ', $detail->getRanking(), PHP_EOL;
}
/* Print
Garry Kasparov: 2851
Anatoly Karpov: 2780
Magnus Carlsen: 2882
Bobby Fischer: 2785
*/
8. Declaraciones de use en grupo
Las clases, funciones y constantes que se importen desde el mismo espacio de nombre ahora pueden ser agrupadas en una única sentencia use.
Agrupando clases
<?php
// Before/Antes PHP 7
// use Symfony\Component\Form\Extension\Core\Type\TextType;
// use Symfony\Component\Form\Extension\Core\Type\DateType;
// use Symfony\Component\Form\Extension\Core\Type\SubmitType;
// PHP 7
use Symfony\Component\Form\Extension\Core\Type\{TextType,DateType,SubmitType};
Agrupando funciones
<?php
namespace WordPress {
function wp_is_writable( $path ) {
if ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) )
return win_is_writable( $path );
else
return @is_writable( $path );
}
function bool_from_yn( $yn ) {
return ( strtolower( $yn ) == 'y' );
}
}
// Global namespace
namespace {
// Before/Antes PHP 7
// use function WordPress\wp_is_writable;
// use function WordPress\bool_from_yn;
// PHP 7
use function WordPress\{wp_is_writable, bool_from_yn};
if (wp_is_writable(__DIR__)) {
echo __DIR__, ' is writeable', PHP_EOL;
}
echo bool_from_yn('y'), PHP_EOL;
}
Agrupando constantes
<?php
namespace Config {
const DB_DRIVER = 'mysql';
const DB_HOST = 'localhost';
const DB_PORT = '3306';
const DB_USER = 'root';
}
// Global namespace
namespace {
// Before/Antes PHP 7
// use const Config\DB_DRIVER;
// use const Config\DB_HOST;
// use const Config\DB_PORT;
// use const Config\DB_USER;
// PHP 7
use const Config\{DB_DRIVER, DB_HOST, DB_PORT, DB_USER};
echo 'Connection details', PHP_EOL;
echo 'Driver: ', DB_DRIVER, ', Host: ', DB_HOST, ', Port: ', DB_PORT, ', user: ', DB_USER, PHP_EOL;
}
9. Generadores
El concepto de generadores se introdujo a partir de PHP 5.5. PHP 7 agrega 2 nuevas características a los generadores
Expresiones return en generadores
Ahora los generadores pueden devolver una expresión final usando la sentencia return (la devolución por referencia no está permitida). Este valor se puede obtener empleando el nuevo método Generator::getReturn(), el cual solamente se puede utilizar una vez que el generador ha finalizado de producir valores.
<?php
$gen = (function() {
yield 1;
yield 2;
return 3;
})();
foreach ($gen as $val) {
echo $val, PHP_EOL;
}
echo $gen->getReturn(), PHP_EOL;
// Print
// 1
// 2
// 3
Delegación de generadores
Los generadores ahora pueden delegar a otro generador, objeto Traversable o array utilizando la sentencia yield from.
<?php
function gen()
{
yield 1;
yield 2;
yield from gen2();
}
function gen2()
{
yield 3;
yield 4;
}
foreach (gen() as $val)
{
echo $val, PHP_EOL;
}
// Print
// 1
// 2
// 3
// 4
10. Sintaxis de escape unicode
Ahora es posible escapar códigos unicode en formato hexadecimal y obtener el símbolo correspondiente. El siguiente script comprueba si el entorno de ejecución es cli e imprime los símbolos correspondientes en caso contrario asume que el resultado se visualizará en el navegador web. Nótese que en caso de visualizar los símbolos en el navagedor se le puede aplicar estilos y usarlo como íconos en botones y otros componentes web.
<?php
$is_cli = false !== strpos(PHP_SAPI, 'cli');
$nl = $is_cli ? PHP_EOL : nl2br(PHP_EOL);
if ($is_cli) {
echo "\u{2665}", $nl;
echo "\u{267B}", $nl;
echo "\u{260E}", $nl;
} else {
$style = '<a href="." style="font-size: 32px; color: %s; text-decoration: none;">%s</span>' . $nl;
echo sprintf($style, 'green', "\u{2665}");
echo sprintf($style, 'red', "\u{267B}");
echo sprintf($style, 'blue', "\u{260E}");
echo '<button type="button">', "\u{267B}", '</button>';
}
/**
* Print
*
* ♥
* ♻
* ☎
*
*/
Conclusión
Ya PHP no es un wrapper del lenguaje C para la web pues las Comunidad va agregando nuevas funcionalidades que hacen que PHP tenga características particulares.
Lectura recomendada