{"id":44032,"date":"2021-09-24T08:37:16","date_gmt":"2021-09-24T06:37:16","guid":{"rendered":"https:\/\/kinsta.com\/?p=100177"},"modified":"2023-08-23T17:28:04","modified_gmt":"2023-08-23T15:28:04","slug":"transpilacion-php","status":"publish","type":"post","link":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/","title":{"rendered":"La Gu\u00eda Definitiva para Transpilar C\u00f3digo PHP"},"content":{"rendered":"<p>En circunstancias ideales, deber\u00edamos utilizar <a href=\"https:\/\/kinsta.com\/es\/changelog\/php-8\/\">PHP 8.0<\/a> (la \u00faltima versi\u00f3n en el momento de escribir este art\u00edculo) para todos nuestros sitios y actualizarlo tan pronto como se publique una nueva versi\u00f3n. Sin embargo, los desarrolladores a menudo necesitar\u00e1n trabajar con versiones anteriores de PHP, como cuando se crea un plugin p\u00fablico para <a href=\"https:\/\/kinsta.com\/es\/blog\/que-es-wordpress\/\">WordPress<\/a> o se trabaja con c\u00f3digo heredado que impide actualizar el entorno del servidor web.<\/p>\n<p>En estas situaciones, podr\u00edamos renunciar a la esperanza de utilizar el \u00faltimo c\u00f3digo PHP. Pero hay una alternativa mejor: podemos seguir escribiendo nuestro c\u00f3digo fuente con PHP 8.0 y transpilarlo a una versi\u00f3n anterior de PHP &#8211; incluso a PHP 7.1.<\/p>\n<p>En esta gu\u00eda, te ense\u00f1aremos todo lo que necesita saber sobre la transpilaci\u00f3n de c\u00f3digo PHP.<\/p>\n<div><\/div><kinsta-auto-toc heading=\"Table of Contents\" exclude=\"last\" list-style=\"arrow\" selector=\"h2\" count-number=\"-1\"><\/kinsta-auto-toc>\n<h2>\u00bfQu\u00e9 es la transpilaci\u00f3n?<\/h2>\n<p>La transpilaci\u00f3n convierte el c\u00f3digo fuente de un <a href=\"https:\/\/kinsta.com\/es\/blog\/mejor-lenguaje-de-programacion-para-aprender\/\">lenguaje de programaci\u00f3n<\/a> en un c\u00f3digo fuente equivalente del mismo o de otro lenguaje de programaci\u00f3n.<\/p>\n<p>La transpilaci\u00f3n no es un concepto nuevo en el desarrollo web: los desarrolladores del lado del cliente probablemente est\u00e9n familiarizados con <a href=\"https:\/\/babeljs.io\/\">Babel<\/a>, un transpilador de c\u00f3digo JavaScript.<\/p>\n<p>Babel convierte el c\u00f3digo JavaScript de la versi\u00f3n moderna de ECMAScript 2015+ en una versi\u00f3n heredada compatible con los navegadores m\u00e1s antiguos. Por ejemplo, dada una funci\u00f3n de flecha de ES2015:<\/p>\n<pre><code class=\"language-js\">[2, 4, 6].map((n) =&gt; n * 2);\n<\/code><\/pre>\n<p>&#8230;Babel lo convertir\u00e1 en su versi\u00f3n ES5:<\/p>\n<pre><code class=\"language-js\">[2, 4, 6].map(function(n) {\n  return n * 2;\n});\n<\/code><\/pre>\n<h3>\u00bfQu\u00e9 es la transpilaci\u00f3n de PHP?<\/h3>\n<p>Lo que es potencialmente nuevo dentro del desarrollo web es la posibilidad de transpilar el c\u00f3digo del lado del servidor, en particular PHP.<\/p>\n<p>La transpilaci\u00f3n de PHP funciona de la misma manera que la transpilaci\u00f3n de JavaScript: el c\u00f3digo fuente de una <a href=\"https:\/\/kinsta.com\/es\/blog\/versiones-de-php\/\">versi\u00f3n moderna de PHP<\/a> se convierte en un c\u00f3digo equivalente para una versi\u00f3n antigua de PHP.<\/p>\n<p>Siguiendo el mismo ejemplo que antes, una funci\u00f3n de flecha de PHP 7.4:<\/p>\n<pre><code class=\"language-php\">$nums = array_map(fn($n) =&gt; $n * 2, [2, 4, 6]);\n<\/code><\/pre>\n<p>&#8230; se puede transpilar a su versi\u00f3n equivalente de PHP 7.3:<\/p>\n<pre><code class=\"language-php\">$nums = array_map(\n  function ($n) {\n    return $n * 2;\n  },\n  [2, 4, 6]\n);\n<\/code><\/pre>\n<p>Las funciones flecha pueden transpilarse porque son <a href=\"https:\/\/en.wikipedia.org\/wiki\/Syntactic_sugar\">az\u00facar sint\u00e1ctico<\/a>, es decir, una nueva sintaxis para producir un comportamiento existente. Esta es la fruta m\u00e1s f\u00e1cil de conseguir.<\/p>\n<p>Sin embargo, tambi\u00e9n hay nuevas caracter\u00edsticas que crean un nuevo comportamiento, y como tal, no habr\u00e1 c\u00f3digo equivalente para las versiones anteriores de PHP. Ese es el caso de <a href=\"https:\/\/kinsta.com\/es\/blog\/php-8\/#union-types-2-0\">los tipos de uni\u00f3n<\/a>, introducidos en PHP 8.0:<\/p>\n<pre><code class=\"language-php\">function someFunction(float|int $param): string|float|int|null\n{\n  \/\/ ...\n}\n<\/code><\/pre>\n<p>En estas situaciones, se puede transpilar siempre que la nueva funci\u00f3n sea necesaria para el desarrollo pero no para la producci\u00f3n. En ese caso, podemos simplemente eliminar la funci\u00f3n del c\u00f3digo transpilado sin consecuencias graves.<\/p>\n<p>Un ejemplo de ello son los tipos de uni\u00f3n. Esta caracter\u00edstica se utiliza para comprobar que no hay un desajuste entre el tipo de entrada y su valor proporcionado, lo que ayuda a prevenir errores. Si hay un conflicto con los tipos, habr\u00e1 un error ya en el desarrollo, y debemos atraparlo y arreglarlo antes de que el c\u00f3digo llegue a producci\u00f3n.<\/p>\n<p>Por lo tanto, podemos permitirnos eliminar la funci\u00f3n del c\u00f3digo para la producci\u00f3n:<\/p>\n<pre><code class=\"language-php\">function someFunction($param)\n{\n  \/\/ ...\n}\n<\/code><\/pre>\n<p>Si el error sigue ocurriendo en producci\u00f3n, el mensaje de error lanzado ser\u00e1 menos preciso que si tuvi\u00e9ramos tipos de uni\u00f3n. Sin embargo, esta posible desventaja se ve compensada por el hecho de poder utilizar tipos de uni\u00f3n en primer lugar.<\/p>\n\n<h2>Ventajas de transpilar el c\u00f3digo PHP<\/h2>\n<p>La transpilaci\u00f3n permite codificar una aplicaci\u00f3n utilizando la \u00faltima versi\u00f3n de PHP y producir una versi\u00f3n que tambi\u00e9n funciona en entornos que ejecutan versiones anteriores de PHP.<\/p>\n<p>Esto puede ser especialmente \u00fatil para los desarrolladores que crean productos para <a href=\"https:\/\/kinsta.com\/es\/blog\/sitema-de-gestion-de-contenido\/\">sistemas de gesti\u00f3n de contenidos (CMS)<\/a> heredados. WordPress, por ejemplo, todav\u00eda <a href=\"https:\/\/wordpress.org\/about\/requirements\/\">soporta oficialmente PHP 5.6<\/a> (aunque recomienda PHP 7.4+). El porcentaje de sitios de WordPress que ejecutan las versiones de PHP 5.6 a 7.2 -que son todas End-of-Life (EOL), lo que significa que ya no reciben actualizaciones de seguridad- se sit\u00faa en un considerable 34,8%, y los que se ejecutan en cualquier versi\u00f3n de PHP distinta de la 8.0 se sit\u00faan en un enorme 99,5%:<\/p>\n<figure id=\"attachment_100180\" aria-describedby=\"caption-attachment-100180\" style=\"width: 920px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/07\/wp-stats.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-100180 size-full\" src=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/07\/wp-stats.png\" alt=\"Estad\u00edsticas de uso de WordPress por versi\u00f3n. Fuente de la imagen: WordPress\" width=\"920\" height=\"720\"><\/a><figcaption id=\"caption-attachment-100180\" class=\"wp-caption-text\">Estad\u00edsticas de uso de WordPress por versi\u00f3n. Fuente de la imagen: <a href=\"https:\/\/wordpress.org\/about\/stats\/\" target=\"_blank\" rel=\"noopener noreferrer\">WordPress<\/a><\/figcaption><\/figure>\n<p>En consecuencia, los temas y plugins de WordPress dirigidos a un p\u00fablico global probablemente se codificar\u00e1n con una versi\u00f3n antigua de PHP para aumentar su posible alcance. Gracias a la transpilaci\u00f3n, estos podr\u00edan codificarse con PHP 8.0 y seguir siendo lanzados para una versi\u00f3n de PHP m\u00e1s antigua, dirigi\u00e9ndose as\u00ed al mayor n\u00famero de usuarios posible.<\/p>\n<p>De hecho, cualquier aplicaci\u00f3n que necesite soportar cualquier versi\u00f3n de PHP que no sea la m\u00e1s reciente (incluso dentro del rango de las versiones de PHP actualmente soportadas) puede beneficiarse.<\/p>\n<p>Este es el caso de Drupal, que <a href=\"https:\/\/www.drupal.org\/docs\/system-requirements\/php-requirements\">requiere PHP 7.3<\/a>. Gracias a la transpilaci\u00f3n, los desarrolladores pueden crear m\u00f3dulos de Drupal disponibles p\u00fablicamente utilizando PHP 8.0, y publicarlos con PHP 7.3.<\/p>\n<p>Otro ejemplo es cuando se crea c\u00f3digo personalizado para clientes que no pueden ejecutar PHP 8.0 en sus entornos por una u otra raz\u00f3n. Sin embargo, gracias a la transpilaci\u00f3n, los desarrolladores pueden seguir codificando sus productos con PHP 8.0 y ejecutarlos en esos entornos heredados.<\/p>\n<h3>Cu\u00e1ndo transpilar PHP<\/h3>\n<p>El c\u00f3digo PHP siempre se puede transpilar a menos que contenga alguna caracter\u00edstica de PHP que no tenga equivalente en la versi\u00f3n anterior de PHP.<\/p>\n<p>Ese es posiblemente el caso de <a href=\"https:\/\/kinsta.com\/es\/blog\/php-8\/#attributes\">los atributos<\/a>, introducidos en PHP 8.0:<\/p>\n<pre><code class=\"language-php\">#[SomeAttr]\nfunction someFunc() {}\n\n#[AnotherAttr]\nclass SomeClass {}\n<\/code><\/pre>\n<p>En el ejemplo anterior en el que se utilizaban funciones de flecha, el c\u00f3digo pod\u00eda transpilarse porque las funciones de flecha son az\u00facar sint\u00e1ctico. Los atributos, en cambio, crean un comportamiento completamente nuevo. Este comportamiento tambi\u00e9n podr\u00eda ser reproducido con PHP 7.4 e inferior, pero solo codific\u00e1ndolo manualmente, es decir, no autom\u00e1ticamente basado en una herramienta o proceso (la IA podr\u00eda proporcionar una soluci\u00f3n, pero a\u00fan no estamos all\u00ed).<\/p>\n<p>Los atributos destinados al uso en desarrollo, como <code><a href=\"https:\/\/wiki.php.net\/rfc\/deprecated_attribute\">#[Deprecated]<\/a><\/code>, pueden eliminarse del mismo modo que los tipos de uni\u00f3n. Pero los atributos que modifican el comportamiento de la aplicaci\u00f3n en producci\u00f3n no se pueden eliminar, y tampoco se pueden transpilar directamente.<\/p>\n<p>A d\u00eda de hoy, ning\u00fan transpilador puede tomar c\u00f3digo con atributos de PHP 8.0 y producir autom\u00e1ticamente su c\u00f3digo equivalente de PHP 7.4. En consecuencia, si tu c\u00f3digo PHP necesita usar atributos, entonces transpilarlo ser\u00e1 dif\u00edcil o inviable.<\/p>\n<h3>Funciones de PHP que se pueden transpilar<\/h3>\n<p>Estas son las caracter\u00edsticas de PHP 7.1 y superiores que actualmente pueden ser transpiladas. Si tu c\u00f3digo solo utiliza estas caracter\u00edsticas, puedes disfrutar de la certeza de que tu aplicaci\u00f3n transpilada funcionar\u00e1. De lo contrario, tendr\u00e1s que evaluar si el c\u00f3digo transpilado producir\u00e1 fallos.<\/p>\n<table>\n<thead>\n<tr>\n<th>Versi\u00f3n PHP<\/th>\n<th>Caracter\u00edsticas<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>7.1<\/td>\n<td>Todo<\/td>\n<\/tr>\n<tr>\n<td>7.2<\/td>\n<td>&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration72.new-features.php#migration72.new-features.object-type\" target=\"_blank\" rel=\"noopener noreferrer\"><code>object<\/code>tipo<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration72.new-features.php#migration72.new-features.param-type-widening\" target=\"_blank\" rel=\"noopener noreferrer\">ampliaci\u00f3n del tipo de par\u00e1metro<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/function.preg-match.php#refsect1-function.preg-match-parameters\" target=\"_blank\" rel=\"noopener noreferrer\"><code>PREG_UNMATCHED_AS_NULL<\/code> bandera en <code>preg_match<\/code><\/a><\/td>\n<\/tr>\n<tr>\n<td>7.3<\/td>\n<td>&#8211; <em><a href=\"https:\/\/www.php.net\/manual\/en\/migration73.new-features.php#migration73.new-features.core.destruct-reference\">Asignaciones de referencia en <code>list()<\/code> \/ desestructuraci\u00f3n de arrays<\/a> (excepto dentro de <code>foreach<\/code> &#8211; <a href=\"https:\/\/github.com\/rectorphp\/rector\/issues\/4376\">#4376<\/a>)<\/em><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration73.new-features.php#migration73.new-features.core.heredoc\" target=\"_blank\" rel=\"noopener noreferrer\">Sintaxis flexible de Heredoc y Nowdoc<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration73.new-features.php#migration73.new-features.core.trailing-commas\" target=\"_blank\" rel=\"noopener noreferrer\">Comas al final de las llamadas a funciones<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration73.other-changes.php#migration73.other-changes.core.setcookie\" target=\"_blank\" rel=\"noopener noreferrer\"><code>set(raw)cookie<\/code> acepta el argumento $option 7.4 <\/a><\/td>\n<\/tr>\n<tr>\n<td><\/td>\n<td>&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.typed-properties\" target=\"_blank\" rel=\"noopener noreferrer\">Propiedades escritas<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/functions.arrow.php\" target=\"_blank\" rel=\"noopener noreferrer\">Funciones de las flechas<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.null-coalescing-assignment-operator\" target=\"_blank\" rel=\"noopener noreferrer\">Operador de asignaci\u00f3n coalescente nulo<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.unpack-inside-array\" target=\"_blank\" rel=\"noopener noreferrer\">Desembalaje dentro de las matrices<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.numeric-literal-separator\" target=\"_blank\" rel=\"noopener noreferrer\">Separador literal num\u00e9rico<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.standard.strip-tags\" target=\"_blank\" rel=\"noopener noreferrer\"><code>strip_tags()<\/code>con una matriz de nombres de etiquetas<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.type-variance\" target=\"_blank\" rel=\"noopener noreferrer\">tipos de retorno covariantes y tipos param contravariantes<\/a><\/td>\n<\/tr>\n<tr>\n<td>8.0<\/td>\n<td>&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/union-types\" target=\"_blank\" rel=\"noopener noreferrer\">Tipos de uni\u00f3n<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/mixed-type\" target=\"_blank\" rel=\"noopener noreferrer\"><code>mixed<\/code> pseudotipo<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/static-return-type\" target=\"_blank\" rel=\"noopener noreferrer\"><code>static<\/code> tipo de retorno<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/class-constant-on-objects\" target=\"_blank\" rel=\"noopener noreferrer\"><code>::class<\/code>constante m\u00e1gica en los objetos<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/match-expression\" target=\"_blank\" rel=\"noopener noreferrer\"><code>match<\/code> expresiones<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/catch-exception-type\" target=\"_blank\" rel=\"noopener noreferrer\"><code>catch<\/code> excepciones s\u00f3lo por tipo<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/null-safe-operator\" target=\"_blank\" rel=\"noopener noreferrer\">Operador a prueba de nulidades<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/constructor-property-promotion\" target=\"_blank\" rel=\"noopener noreferrer\">Promoci\u00f3n de la propiedad del constructor de la clase<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/trailing-comma-parameter-use-list\" target=\"_blank\" rel=\"noopener noreferrer\">Comas al final de las listas de par\u00e1metros y listas de <code>use<\/code> de cierres lists\u00a0<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\u00a0<\/p>\n<h2>Transpiladores PHP<\/h2>\n<p>Actualmente, existe una herramienta para transpilar c\u00f3digo PHP: <a href=\"https:\/\/github.com\/rectorphp\/rector\">Rector<\/a>.<\/p>\n<p>Rector es una herramienta de reconstrucci\u00f3n de PHP, que convierte el c\u00f3digo PHP basado en reglas programables. Introducimos el c\u00f3digo fuente y el <a href=\"https:\/\/github.com\/rectorphp\/rector\/blob\/main\/docs\/rector_rules_overview.md\">conjunto de reglas<\/a> a ejecutar, y Rector transformar\u00e1 el c\u00f3digo.<\/p>\n<p>Rector se maneja a trav\u00e9s de la l\u00ednea de comandos, instalada en el proyecto a trav\u00e9s de Composer. Cuando se ejecuta, Rector mostrar\u00e1 un \u00abdiff\u00bb (adiciones en verde, eliminaciones en rojo) del c\u00f3digo antes y despu\u00e9s de la conversi\u00f3n:<\/p>\n<figure id=\"attachment_100178\" aria-describedby=\"caption-attachment-100178\" style=\"width: 740px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/07\/rector-process-dry-run.gif\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-100178 size-full\" src=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/07\/rector-process-dry-run.gif\" alt=\"Salida \"diff\" de Rector\" width=\"740\" height=\"480\"><\/a><figcaption id=\"caption-attachment-100178\" class=\"wp-caption-text\">Salida \u00abdiff\u00bb de Rector<\/figcaption><\/figure>\n<h2>Qu\u00e9 versiones de PHP hay que transpilar<\/h2>\n<p>Para transpilar el c\u00f3digo a trav\u00e9s de las versiones de PHP, se deben crear las reglas correspondientes.<\/p>\n<p>Hoy en d\u00eda, la biblioteca Rector incluye la mayor\u00eda de las reglas para transpilar c\u00f3digo dentro del rango de PHP 8.0 a 7.1. Por lo tanto, podemos transpilar de forma fiable nuestro c\u00f3digo PHP hasta la versi\u00f3n 7.1.<\/p>\n<p>Tambi\u00e9n hay <a href=\"https:\/\/github.com\/rectorphp\/rector\/blob\/main\/docs\/rector_rules_overview.md#downgradephp71\">reglas para transpilar de PHP 7.1a7.0<\/a> y <a href=\"https:\/\/github.com\/rectorphp\/rector\/blob\/main\/docs\/rector_rules_overview.md#downgradephp70\">de 7.0 a 5.6<\/a>, pero no son exhaustivas. Se est\u00e1 trabajando para completarlas, por lo que eventualmente podremos transpilar c\u00f3digo PHP hasta la versi\u00f3n 5.6.<\/p>\n<h2>Transpilaje vs. Backporting<\/h2>\n<p>El backporting es similar al transpiling, pero m\u00e1s sencillo. La backporting de c\u00f3digo no depende necesariamente de las nuevas caracter\u00edsticas de un lenguaje. En su lugar, se puede proporcionar la misma funcionalidad a una versi\u00f3n anterior del lenguaje simplemente copiando\/pegando\/adaptando el c\u00f3digo correspondiente de la nueva versi\u00f3n del lenguaje.<\/p>\n<p>Por ejemplo, la funci\u00f3n <code>str_contains<\/code> se introdujo en PHP 8.0. La misma funci\u00f3n para PHP 7.4 e inferior se puede implementar f\u00e1cilmente as\u00ed:<\/p>\n<pre><code class=\"language-php\">if (!defined('PHP_VERSION_ID') || (defined('PHP_VERSION_ID') && PHP_VERSION_ID &lt; 80000)) {\n  if (!function_exists('str_contains')) {\n    \/**\n     * Checks if a string contains another\n     *\n     * @param string $haystack The string to search in\n     * @param string $needle The string to search\n     * @return boolean Returns TRUE if the needle was found in haystack, FALSE otherwise.\n     *\/\n    function str_contains(string $haystack, string $needle): bool\n    {\n      return strpos($haystack, $needle) !== false;\n    }\n  }\n}\n<\/code><\/pre>\n<p>Dado que el backporting es m\u00e1s sencillo que la transpilaci\u00f3n, deber\u00edamos optar por esta soluci\u00f3n siempre que el backporting haga el trabajo.<\/p>\n<p>En cuanto al rango entre PHP 8.0 y 7.1, podemos utilizar las librer\u00edas polyfill de <a href=\"https:\/\/symfony.com\/\">Symfony<\/a>:<\/p>\n<ul>\n<li style=\"list-style-type: none\">\n<ul>\n<li><a href=\"https:\/\/github.com\/symfony\/polyfill-php71\/\" target=\"_blank\" rel=\"noopener noreferrer\">Polyfill PHP 7.1<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/symfony\/polyfill-php72\/\" target=\"_blank\" rel=\"noopener noreferrer\">Polyfill PHP 7.2<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/symfony\/polyfill-php73\/\" target=\"_blank\" rel=\"noopener noreferrer\">Polyfill PHP 7.3<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<ul>\n<li><a href=\"https:\/\/github.com\/symfony\/polyfill-php74\/\" target=\"_blank\" rel=\"noopener noreferrer\">Polyfill PHP 7.4<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/symfony\/polyfill-php80\/\" target=\"_blank\" rel=\"noopener noreferrer\">Polyfill PHP 8.0<\/a><\/li>\n<\/ul>\n<p>Estas bibliotecas soportan las siguientes funciones, clases, constantes e interfaces:<\/p>\n<table style=\"height: 1173px\" width=\"335\">\n<thead>\n<tr>\n<th>Versi\u00f3n PHP<\/th>\n<th>Caracter\u00edsticas<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>7.2<\/td>\n<td>Funciones:<\/p>\n<ul>\n<li><a href=\"https:\/\/php.net\/spl_object_id\" target=\"_blank\" rel=\"noopener noreferrer\"><code>spl_object_id<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/utf8_encode\" target=\"_blank\" rel=\"noopener noreferrer\"><code>utf8_encode<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/utf8_decode\" target=\"_blank\" rel=\"noopener noreferrer\"><code>utf8_decode<\/code><\/a><\/li>\n<\/ul>\n<p>Constantes:<\/p>\n<ul>\n<li><a href=\"https:\/\/php.net\/reserved.constants#constant.php-float-dig\" target=\"_blank\" rel=\"noopener noreferrer\"><code>PHP_FLOAT_*<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/reserved.constants#constant.php-os-family\" target=\"_blank\" rel=\"noopener noreferrer\"><code>PHP_OS_FAMILY<\/code><\/a><\/li>\n<\/ul>\n<\/td>\n<\/tr>\n<tr>\n<td>7.3<\/td>\n<td>Funciones:<\/p>\n<ul>\n<li><a href=\"https:\/\/php.net\/array_key_first\" target=\"_blank\" rel=\"noopener noreferrer\"><code>array_key_first<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/array_key_last\" target=\"_blank\" rel=\"noopener noreferrer\"><code>array_key_last<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/function.hrtime\" target=\"_blank\" rel=\"noopener noreferrer\"><code>hrtime<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/is_countable\" target=\"_blank\" rel=\"noopener noreferrer\"><code>is_countable<\/code><\/a><\/li>\n<\/ul>\n<p>Excepciones:<\/p>\n<ul>\n<li><a href=\"https:\/\/php.net\/JsonException\" target=\"_blank\" rel=\"noopener noreferrer\"><code>JsonException<\/code><\/a><\/li>\n<\/ul>\n<\/td>\n<\/tr>\n<tr>\n<td>7.4<\/td>\n<td>Funciones:<\/p>\n<ul>\n<li><a href=\"https:\/\/php.net\/get_mangled_object_vars\" target=\"_blank\" rel=\"noopener noreferrer\"><code>get_mangled_object_vars<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/mb_str_split\" target=\"_blank\" rel=\"noopener noreferrer\"><code>mb_str_split<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/password_algos\" target=\"_blank\" rel=\"noopener noreferrer\"><code>password_algos<\/code><\/a><\/li>\n<\/ul>\n<\/td>\n<\/tr>\n<tr>\n<td>8.0<\/td>\n<td>Interfaces:<\/p>\n<ul>\n<li><code>Stringable<\/code><\/li>\n<\/ul>\n<p>Clases:<\/p>\n<ul>\n<li><code>ValueError<\/code><\/li>\n<li><code>UnhandledMatchError<\/code><\/li>\n<\/ul>\n<p>Constantes:<\/p>\n<ul>\n<li><code>FILTER_VALIDATE_BOOL<\/code><\/li>\n<\/ul>\n<p>Funciones:<\/p>\n<ul>\n<li><a href=\"https:\/\/php.net\/fdiv\" target=\"_blank\" rel=\"noopener noreferrer\"><code>fdiv<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/get_debug_type\" target=\"_blank\" rel=\"noopener noreferrer\"><code>get_debug_type<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/preg_last_error_msg\" target=\"_blank\" rel=\"noopener noreferrer\"><code>preg_last_error_msg<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/str_contains\" target=\"_blank\" rel=\"noopener noreferrer\"><code>str_contains<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/str_starts_with\" target=\"_blank\" rel=\"noopener noreferrer\"><code>str_starts_with<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/str_ends_with\" target=\"_blank\" rel=\"noopener noreferrer\"><code>str_ends_with<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/get_resource_id\" target=\"_blank\" rel=\"noopener noreferrer\"><code>get_resource_id<\/code><\/a><\/li>\n<\/ul>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Ejemplos de PHP transpilado<\/h2>\n<p>Vamos a inspeccionar algunos ejemplos de c\u00f3digo PHP transpilado, y algunos paquetes que est\u00e1n siendo totalmente transpilados.<\/p>\n<h3>C\u00f3digo PHP<\/h3>\n<p>La expresi\u00f3n de <code>match<\/code> fue <a href=\"https:\/\/kinsta.com\/es\/blog\/php-8\/#match-expression\">introducida en PHP 8.0<\/a>. Este c\u00f3digo fuente:<\/p>\n<pre><code class=\"language-php\">function getFieldValue(string $fieldName): ?string\n{\n  return match($fieldName) {\n    'foo' =&gt; 'foofoo',\n    'bar' =&gt; 'barbar',\n    'baz' =&gt; 'bazbaz',\n    default =&gt; null,\n  };\n}\n<\/code><\/pre>\n<p>&#8230; se transpilar\u00e1 a su versi\u00f3n equivalente de PHP 7.4, utilizando el operador <code>switch<\/code>:<\/p>\n<pre><code class=\"language-php\">function getFieldValue(string $fieldName): ?string\n{\n  switch ($fieldName) {\n    case 'foo':\n      return 'foofoo';\n    case 'bar':\n      return 'barbar';\n    case 'baz':\n      return 'bazbaz';\n    default:\n      return null;\n  }\n}\n<\/code><\/pre>\n<p>El <a href=\"https:\/\/kinsta.com\/es\/blog\/php-8\/#nullsafe-operator\">operador nullsafe<\/a> tambi\u00e9n fue introducido en PHP 8.0:<\/p>\n<pre><code class=\"language-php\">public function getValue(TypeResolverInterface $typeResolver): ?string\n{\n  return $this-&gt;getResolver($typeResolver)?-&gt;getValue();\n}\n<\/code><\/pre>\n<p>El c\u00f3digo transpilado debe asignar primero el valor de la operaci\u00f3n a una nueva variable, para evitar ejecutar la operaci\u00f3n dos veces:<\/p>\n<pre><code class=\"language-php\">public function getValue(TypeResolverInterface $typeResolver): ?string\n{\n  return ($val = $this-&gt;getResolver($typeResolver)) ? $val-&gt;getValue() : null;\n}<\/code><\/pre>\n<p>La caracter\u00edstica de <a href=\"https:\/\/kinsta.com\/es\/blog\/php-8\/#constructor-property-promotion\">promoci\u00f3n de propiedades del constructor<\/a>, tambi\u00e9n introducida en PHP 8.0, permite a los desarrolladores escribir menos c\u00f3digo:<\/p>\n<pre><code class=\"language-php\">class QueryResolver\n{\n  function __construct(protected QueryFormatter $queryFormatter)\n  {\n  }\n}\n<\/code><\/pre>\n<p>Al transpilarlo para PHP 7.4, se produce el c\u00f3digo completo:<\/p>\n<pre><code class=\"language-php\"> class QueryResolver\n {\n  protected QueryFormatter $queryFormatter;\n\n  function __construct(QueryFormatter $queryFormatter)\n  {\n    $this-&gt;queryFormatter = $queryFormatter;\n  }\n}\n<\/code><\/pre>\n<p>El c\u00f3digo transpilado arriba contiene <a href=\"https:\/\/kinsta.com\/es\/blog\/php-7-4\/#typed-properties\">propiedades tipadas<\/a>, que fueron introducidas en PHP 7.4. La transpilaci\u00f3n de ese c\u00f3digo a PHP 7.3 las sustituye por docblocks:<\/p>\n<pre><code class=\"language-php\"> class QueryResolver\n {\n  \/**\n   * @var QueryFormatter\n   *\/\n  protected $queryFormatter;\n\n  function __construct(QueryFormatter $queryFormatter)\n  {\n    $this-&gt;queryFormatter = $queryFormatter;\n  }\n}\n<\/code><\/pre>\n<h3>Paquetes PHP<\/h3>\n<p>Las siguientes bibliotecas est\u00e1n siendo transpiladas para producci\u00f3n:<\/p>\n<table>\n<thead>\n<tr>\n<th>Biblioteca\/descripci\u00f3n<\/th>\n<th>C\u00f3digo\/notas<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><a href=\"https:\/\/getrector.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">Rector<\/a><br \/>\nHerramienta de reconstrucci\u00f3n de PHP que hace posible la transpilaci\u00f3n<\/td>\n<td>&#8211; <a href=\"https:\/\/github.com\/rectorphp\/rector-src\" target=\"_blank\" rel=\"noopener noreferrer\">C\u00f3digo fuente<\/a><br \/>\n&#8211; <a href=\"https:\/\/github.com\/rectorphp\/rector\" target=\"_blank\" rel=\"noopener noreferrer\">C\u00f3digo transpilado<\/a><br \/>\n&#8211; <a href=\"https:\/\/getrector.org\/blog\/prefixed-rector-by-default\" target=\"_blank\" rel=\"noopener noreferrer\">Notas<\/a><\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/tomasvotruba.com\/blog\/2017\/05\/03\/combine-power-of-php-code-sniffer-and-php-cs-fixer-in-3-lines\/\" target=\"_blank\" rel=\"noopener noreferrer\">Normas de codificaci\u00f3n sencillas<\/a><br \/>\nHerramienta para que el c\u00f3digo PHP se adhiera a un conjunto de reglas<\/td>\n<td>&#8211; <a href=\"https:\/\/github.com\/symplify\/symplify\/tree\/main\/packages\/easy-coding-standard\" target=\"_blank\" rel=\"noopener noreferrer\">C\u00f3digo fuente<\/a><br \/>\n&#8211; <a href=\"https:\/\/github.com\/symplify\/easy-coding-standard\" target=\"_blank\" rel=\"noopener noreferrer\">C\u00f3digo transpliado<\/a><br \/>\n&#8211; <a href=\"https:\/\/tomasvotruba.com\/blog\/introducing-ecs-prefixed-and-downgraded-to-php-71\/\" target=\"_blank\" rel=\"noopener noreferrer\">Notas<\/a><\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/graphql-api.com\" target=\"_blank\" rel=\"noopener noreferrer\">API GraphQL para WordPress<\/a><br \/>\nPlugin que proporciona un servidor GraphQL para WordPress<\/td>\n<td>&#8211; <a href=\"https:\/\/github.com\/leoloso\/PoP\/tree\/master\/layers\/GraphQLAPIForWP\/plugins\/graphql-api-for-wp\" target=\"_blank\" rel=\"noopener noreferrer\">C\u00f3digo fuente<\/a><br \/>\n&#8211; <a href=\"https:\/\/github.com\/GraphQLAPI\/graphql-api-for-wp-dist\/\" target=\"_blank\" rel=\"noopener noreferrer\">C\u00f3digo transpilado<\/a><br \/>\n&#8211; <a href=\"https:\/\/graphql-api.com\/blog\/the-plugin-is-now-transpiled-from-php-80-to-71\/\" target=\"_blank\" rel=\"noopener noreferrer\">Notes<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Ventajas y desventajas de transpilar PHP<\/h2>\n<p>La ventaja de transpilar PHP ya se ha descrito: permite que el c\u00f3digo fuente utilice PHP 8.0 (es decir, la \u00faltima versi\u00f3n de PHP), que se transformar\u00e1 en una versi\u00f3n inferior para que PHP se ejecute en producci\u00f3n en una aplicaci\u00f3n o entorno heredado.<\/p>\n<p>Esto nos permite efectivamente ser mejores desarrolladores, produciendo c\u00f3digo de mayor calidad. Esto se debe a que nuestro c\u00f3digo fuente puede utilizar los tipos de uni\u00f3n de PHP 8.0, las propiedades tipadas de PHP 7.4 y los diferentes tipos y pseudotipos a\u00f1adidos a cada nueva versi\u00f3n de PHP (<code>mixed<\/code> de PHP 8.0, <code>objetc<\/code> de PHP 7.2), entre otras caracter\u00edsticas modernas de PHP.<\/p>\n<p>Gracias a estas funciones, podemos detectar mejor los errores durante el desarrollo y escribir un c\u00f3digo m\u00e1s f\u00e1cil de leer.<\/p>\n<p>Ahora, veamos los inconvenientes.<\/p>\n<h3>Debe ser codificado y mantenido<\/h3>\n<p>Rector puede transpilar el c\u00f3digo autom\u00e1ticamente, pero el proceso probablemente requerir\u00e1 alguna entrada manual para que funcione con nuestra configuraci\u00f3n espec\u00edfica.<\/p>\n<h3>Las bibliotecas de terceros tambi\u00e9n deben ser transpiladas<\/h3>\n<p>Esto se convierte en un problema cuando al transpilarlos se producen errores, ya que entonces debemos profundizar en su c\u00f3digo fuente para averiguar la posible raz\u00f3n. Si el problema se puede solucionar y el proyecto es de c\u00f3digo abierto, tendremos que enviar una solicitud de extracci\u00f3n. Si la biblioteca no es de c\u00f3digo abierto, podemos encontrarnos con un obst\u00e1culo.<\/p>\n<h3>El rector no nos informa cuando el c\u00f3digo no puede ser transpuesto<\/h3>\n<p>Si el c\u00f3digo fuente contiene atributos de PHP 8.0 o cualquier otra caracter\u00edstica que no pueda ser transpilada, no podemos proceder. Sin embargo, Rector no comprobar\u00e1 esta condici\u00f3n, por lo que tendremos que hacerlo manualmente. Esto puede no ser un gran problema en relaci\u00f3n con nuestro propio c\u00f3digo fuente, ya que estamos familiarizados con \u00e9l, pero podr\u00eda convertirse en un obst\u00e1culo en relaci\u00f3n con las dependencias de terceros.<\/p>\n<h3>La informaci\u00f3n de depuraci\u00f3n utiliza el c\u00f3digo transpilado, no el c\u00f3digo fuente<\/h3>\n<p>Cuando la aplicaci\u00f3n produce un mensaje de error con un seguimiento de pila en producci\u00f3n, el n\u00famero de l\u00ednea apuntar\u00e1 al c\u00f3digo transpilado. Tenemos que volver a convertir el c\u00f3digo transpilado al original para encontrar el n\u00famero de l\u00ednea correspondiente en el c\u00f3digo fuente.<\/p>\n<h3>El c\u00f3digo transpilado tambi\u00e9n debe llevar un prefijo<\/h3>\n<p>Nuestro proyecto transpilado y alguna otra biblioteca tambi\u00e9n instalada en el entorno de producci\u00f3n podr\u00edan utilizar la misma dependencia de terceros. Esta dependencia de terceros ser\u00e1 transpilada para nuestro proyecto y mantendr\u00e1 su c\u00f3digo fuente original para la otra biblioteca. Por lo tanto, la versi\u00f3n transpilada debe ser prefijada a trav\u00e9s de <a href=\"https:\/\/github.com\/humbug\/php-scoper\">PHP-Scoper<\/a>, <a href=\"https:\/\/github.com\/BrianHenryIE\/strauss\">Strauss<\/a>, o alguna otra herramienta para evitar potenciales conflictos.<\/p>\n<h3>La transpilaci\u00f3n debe realizarse durante la integraci\u00f3n continua (CI)<\/h3>\n<p>Debido a que el c\u00f3digo transpilado naturalmente anular\u00e1 el c\u00f3digo fuente, no debemos ejecutar el proceso de transpilaci\u00f3n en nuestros ordenadores de desarrollo, o nos arriesgaremos a crear efectos secundarios. Ejecutar el proceso durante una ejecuci\u00f3n de CI es m\u00e1s adecuado (m\u00e1s sobre esto m\u00e1s adelante).<\/p>\n<h2>C\u00f3mo transpilar PHP<\/h2>\n<p>En primer lugar, tenemos que instalar Rector en nuestro proyecto para el desarrollo:<\/p>\n<pre><code class=\"language-bash\">composer require rector\/rector --dev\n<\/code><\/pre>\n<p>A continuaci\u00f3n, creamos un archivo de configuraci\u00f3n <code>rector.php<\/code> en el directorio ra\u00edz del proyecto que contiene los conjuntos de reglas necesarios. Para bajar el c\u00f3digo de PHP 8.0 a 7.1, usamos esta configuraci\u00f3n:<\/p>\n<pre><code class=\"language-php\">use Rector\\Set\\ValueObject\\DowngradeSetList;\nuse Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator;\n\nreturn static function (ContainerConfigurator $containerConfigurator): void {\n    $containerConfigurator-&gt;import(DowngradeSetList::PHP_80);\n    $containerConfigurator-&gt;import(DowngradeSetList::PHP_74);\n    $containerConfigurator-&gt;import(DowngradeSetList::PHP_73);\n    $containerConfigurator-&gt;import(DowngradeSetList::PHP_72);\n};\n<\/code><\/pre>\n<p>Para asegurarnos de que el proceso se ejecuta como se espera, podemos ejecutar el comando de <code>process<\/code> de Rector en modo seco, pasando la(s) ubicaci\u00f3n(es) a procesar (en este caso, todos los archivos bajo la carpeta <code>src\/<\/code>):<\/p>\n<pre><code class=\"language-bash\">vendor\/bin\/rector process src --dry-run\n<\/code><\/pre>\n<p>Para realizar la transpilaci\u00f3n, ejecutamos el comando de <code>process<\/code> de Rector, que modificar\u00e1 los archivos en su ubicaci\u00f3n actual:<\/p>\n<pre><code class=\"language-bash\">vendor\/bin\/rector process src\n<\/code><\/pre>\n<p>Ten en cuenta: si ejecutamos el\u00a0 <code>rector process<\/code> en nuestros ordenadores de desarrollo, el c\u00f3digo fuente se convertir\u00e1 en su lugar, bajo <code>src\/<\/code>. Sin embargo, queremos producir el c\u00f3digo convertido en una ubicaci\u00f3n diferente para no sobrepasar el c\u00f3digo fuente cuando se degrade el c\u00f3digo. Por esta raz\u00f3n, ejecutar el proceso es m\u00e1s adecuado durante la integraci\u00f3n continua.<\/p>\n<h2>Optimizaci\u00f3n del proceso de transpilaci\u00f3n<\/h2>\n<p>Para generar un entregable transpilado para producci\u00f3n, solo hay que convertir el c\u00f3digo para producci\u00f3n; se puede omitir el c\u00f3digo necesario solo para desarrollo. Esto significa que podemos evitar transpilar todas las pruebas (tanto para nuestro proyecto como para sus dependencias) y todas las dependencias para el desarrollo.<\/p>\n<p>En cuanto a las pruebas, ya sabremos d\u00f3nde se encuentran las de nuestro proyecto, por ejemplo, en la carpeta <code>tests\/<\/code>. Tambi\u00e9n debemos averiguar d\u00f3nde est\u00e1n los de las dependencias &#8211; por ejemplo, en sus subcarpetas <code>tests\/<\/code>, <code>test\/<\/code> y <code>Test\/<\/code> (para diferentes librer\u00edas). Entonces, le decimos a Rector que omita el procesamiento de estas carpetas:<\/p>\n<pre><code class=\"language-php\">return static function (ContainerConfigurator $containerConfigurator): void {\n  \/\/ ...\n\n  $parameters-&gt;set(Option::SKIP, [\n    \/\/ Skip tests\n    '*\/tests\/*',\n    '*\/test\/*',\n    '*\/Test\/*',\n  ]);\n};<\/code><\/pre>\n<p>Con respecto a las dependencias, Composer sabe cu\u00e1les son para el desarrollo (las que est\u00e1n bajo la entrada <code>require-dev<\/code> en <code>composer.json<\/code>) y cu\u00e1les son para producci\u00f3n (las que est\u00e1n bajo la entrada <code>require<\/code>).<\/p>\n<p>Para recuperar de Composer las rutas de todas las dependencias para la producci\u00f3n, ejecutamos:<\/p>\n<pre><code class=\"language-bash\">composer info --path --no-dev\n<\/code><\/pre>\n<p>Este comando producir\u00e1 una lista de dependencias con su nombre y ruta de acceso, as\u00ed:<\/p>\n<pre><code>brain\/cortex                     \/Users\/leo\/GitHub\/leoloso\/PoP\/vendor\/brain\/cortex\ncomposer\/installers              \/Users\/leo\/GitHub\/leoloso\/PoP\/vendor\/composer\/installers\ncomposer\/semver                  \/Users\/leo\/GitHub\/leoloso\/PoP\/vendor\/composer\/semver\nguzzlehttp\/guzzle                \/Users\/leo\/GitHub\/leoloso\/PoP\/vendor\/guzzlehttp\/guzzle\nleague\/pipeline                  \/Users\/leo\/GitHub\/leoloso\/PoP\/vendor\/league\/pipeline\n<\/code><\/pre>\n<p>Podemos extraer todas las rutas e introducirlas en el comando Rector, que procesar\u00e1 la carpeta src\/ de nuestro proyecto m\u00e1s aquellas carpetas que contengan todas las dependencias para producci\u00f3n:<\/p>\n<pre><code class=\"language-bash\">$ paths=\"$(composer info --path --no-dev | cut -d' ' -f2- | sed 's\/ \/\/g' | tr '\\n' ' ')\"\n$ vendor\/bin\/rector process src $paths\n<\/code><\/pre>\n<p>Una mejora adicional puede evitar que Rector procese aquellas dependencias que ya utilizan la versi\u00f3n de PHP de destino. Si una biblioteca ha sido codificada con PHP 7.1 (o cualquier versi\u00f3n inferior), entonces no hay necesidad de transpilarla a PHP 7.1.<\/p>\n<p>Para lograr esto, podemos obtener la lista de bibliotecas que requieren PHP 7.2 y superior y procesar solo esas. Obtendremos los nombres de todas estas bibliotecas a trav\u00e9s del comando <code>why-not<\/code> de Composer, as\u00ed:<\/p>\n<pre><code class=\"language-bash\">composer why-not php \"7.1.*\" | grep -o \"\\S*\\\/\\S*\"\n<\/code><\/pre>\n<p>Debido a que este comando no funciona con la bandera <code>--no-dev<\/code>, para incluir solo las dependencias para producci\u00f3n, primero tenemos que eliminar las dependencias para desarrollo y regenerar el autoloader, ejecutar el comando, y luego a\u00f1adirlas de nuevo:<\/p>\n<pre><code class=\"language-bash\">$ composer install --no-dev\n$ packages=$(composer why-not php \"7.1.*\" | grep -o \"\\S*\\\/\\S*\")\n$ composer install\n<\/code><\/pre>\n<p>El comando <code>info --path<\/code> de Composer recupera la ruta de un paquete, con este formato:<\/p>\n<pre><code class=\"language-bash\"># Executing this command\n$ composer info psr\/cache --path   \n# Produces this response:\npsr\/cache \/Users\/leo\/GitHub\/leoloso\/PoP\/vendor\/psr\/cache\n<\/code><\/pre>\n<p>Ejecutamos este comando para todos los elementos de nuestra lista para obtener todas las rutas a transpilar:<\/p>\n<pre><code class=\"language-bash\">for package in $packages\ndo\n  path=$(composer info $package --path | cut -d' ' -f2-)\n  paths=\"$paths $path\"\ndone\n<\/code><\/pre>\n<p>Finalmente, proporcionamos esta lista al Rector (adem\u00e1s de la carpeta <code>src\/<\/code> del proyecto):<\/p>\n<pre><code class=\"language-bash\">vendor\/bin\/rector process src $paths\n<\/code><\/pre>\n<h2>Errores que debemos evitar al transpilar c\u00f3digo<\/h2>\n<p>Transpilar c\u00f3digo podr\u00eda considerarse un arte, que a menudo requiere ajustes espec\u00edficos para el proyecto. Veamos algunos problemas con los que nos podemos encontrar.<\/p>\n<h3>Las reglas encadenadas no siempre se procesan<\/h3>\n<p>Una regla encadenada es cuando una regla necesita convertir el c\u00f3digo producido por una regla anterior.<\/p>\n<p>Por ejemplo, la biblioteca <code>symfony\/cache<\/code> contiene <a href=\"https:\/\/github.com\/symfony\/cache\/blob\/be5707f\/CacheItem.php#L115\">este c\u00f3digo<\/a>:<\/p>\n<pre><code class=\"language-php\">final class CacheItem implements ItemInterface\n{\n  public function tag($tags): ItemInterface\n  {\n    \/\/ ...\n    return $this;\n  }\n}\n<\/code><\/pre>\n<p>l transpilar de PHP 7.4 a 7.3,\u00a0 <code>tag<\/code> de la funci\u00f3n debe sufrir dos modificaciones:<\/p>\n<ul>\n<li>El tipo de retorno <code>ItemInterface<\/code> debe convertirse primero en <code>self<\/code>, debido a la regla <a href=\"https:\/\/github.com\/rectorphp\/rector-src\/blob\/f451b0b\/rules\/DowngradePhp74\/Rector\/ClassMethod\/DowngradeCovariantReturnTypeRector.php\"><code>DowngradeCovariantReturnTypeRector<\/code><\/a><\/li>\n<li>El tipo de retorno <code>self<\/code> debe entonces ser eliminado, debido a la regla <a href=\"https:\/\/github.com\/rectorphp\/rector-src\/blob\/f451b0b\/rules\/DowngradePhp74\/Rector\/ClassMethod\/DowngradeSelfTypeDeclarationRector.php\"><code>DowngradeSelfTypeDeclarationRector<\/code><\/a><\/li>\n<\/ul>\n<p>El resultado final deber\u00eda ser \u00e9ste:<\/p>\n<pre><code class=\"language-php\">final class CacheItem implements ItemInterface\n{\n  public function tag($tags)\n  {\n    \/\/ ...\n    return $this;\n  }\n}\n<\/code><\/pre>\n<p>Sin embargo, el rector solo da salida a la etapa intermedia:<\/p>\n<pre><code class=\"language-php\">final class CacheItem implements ItemInterface\n{\n  public function tag($tags): self\n  {\n    \/\/ ...\n    return $this;\n  }\n}<\/code><\/pre>\n<p>La cuesti\u00f3n es que <a href=\"https:\/\/github.com\/rectorphp\/rector\/issues\/5962\">el rector no siempre puede controlar el orden de aplicaci\u00f3n de las normas<\/a>.<\/p>\n<p>La soluci\u00f3n es identificar qu\u00e9 reglas encadenadas quedaron sin procesar y ejecutar una nueva ejecuci\u00f3n de Rector para aplicarlas.<\/p>\n<p>Para identificar las reglas encadenadas, ejecutamos Rector dos veces en el c\u00f3digo fuente, as\u00ed:<\/p>\n<pre><code class=\"language-bash\">$ vendor\/bin\/rector process src\n$ vendor\/bin\/rector process src --dry-run\n<\/code><\/pre>\n<p>La primera vez, ejecutamos Rector como se espera, para ejecutar la transpilaci\u00f3n. La segunda vez, usamos la bandera <code>--dry-run<\/code> para descubrir si todav\u00eda hay cambios que hacer. Si los hay, el comando saldr\u00e1 con un c\u00f3digo de error, y la salida \u00abdiff\u00bb indicar\u00e1 qu\u00e9 regla(s) puede(n) aplicarse todav\u00eda. Esto significar\u00eda que la primera ejecuci\u00f3n no se ha completado, con alguna regla encadenada que no se ha procesado.<\/p>\n<figure id=\"attachment_100179\" aria-describedby=\"caption-attachment-100179\" style=\"width: 901px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/07\/rector-process-run-dry.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-100179 size-full\" src=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/07\/rector-process-run-dry.png\" alt=\"Ejecuci\u00f3n de Rector con la bandera --dry-run\" width=\"901\" height=\"903\"><\/a><figcaption id=\"caption-attachment-100179\" class=\"wp-caption-text\">Ejecuci\u00f3n de Rector con la bandera &#8211;dry-run<\/figcaption><\/figure>\n<p>Una vez que hayamos identificado la regla (o reglas) encadenada sin aplicar, podemos crear otro archivo de configuraci\u00f3n de Rector &#8211; por ejemplo, <code>rector-chained-rule.php<\/code> ejecutar\u00e1 la regla que falta. En lugar de procesar un conjunto completo de reglas para todos los archivos bajo <code>src\/<\/code>, esta vez, podemos ejecutar la regla espec\u00edfica que falta en el archivo espec\u00edfico donde debe aplicarse:<\/p>\n<pre><code class=\"language-php\">\/\/ rector-chained-rule.php\nuse Rector\\Core\\Configuration\\Option;\nuse Rector\\DowngradePhp74\\Rector\\ClassMethod\\DowngradeSelfTypeDeclarationRector;\nuse Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator;\n\nreturn static function (ContainerConfigurator $containerConfigurator): void {\n  $services = $containerConfigurator-&gt;services();\n  $services-&gt;set(DowngradeSelfTypeDeclarationRector::class);\n\n  $parameters = $containerConfigurator-&gt;parameters();\n  $parameters-&gt;set(Option::PATHS, [\n    __DIR__ . '\/vendor\/symfony\/cache\/CacheItem.php',\n  ]);\n};\n<\/code><\/pre>\n<p>Finalmente, le decimos a Rector en su segunda pasada que utilice el nuevo archivo de configuraci\u00f3n mediante la entrada <code>--config<\/code>:<\/p>\n<pre><code class=\"language-bash\"># First pass with all modifications\n$ vendor\/bin\/rector process src\n\n# Second pass to fix a specific problem\n$ vendor\/bin\/rector process --config=rector-chained-rule.php\n<\/code><\/pre>\n<h3>Las dependencias de Composer pueden ser inconsistentes<\/h3>\n<p>Las librer\u00edas pueden declarar una dependencia para ser destinada al desarrollo (es decir, bajo <code>require-dev<\/code> en <code>composer.json<\/code>), y a\u00fan as\u00ed, referenciar alg\u00fan c\u00f3digo de ellas para producci\u00f3n (como en algunos archivos bajo <code>src\/<\/code>, no <code>tests<\/code>\/).<\/p>\n<p>Normalmente, esto no es un problema porque ese c\u00f3digo puede no cargarse en producci\u00f3n, por lo que nunca habr\u00e1 un error en la aplicaci\u00f3n. Sin embargo, cuando Rector procesa el c\u00f3digo fuente y sus dependencias, valida que todo el c\u00f3digo referenciado pueda ser cargado. Rector arrojar\u00e1 un error si alg\u00fan archivo hace referencia a alguna pieza de c\u00f3digo de una biblioteca no instalada (porque fue declarada como necesaria s\u00f3lo para el desarrollo).<\/p>\n<p>Por ejemplo, la clase <a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/8f03a1f\/src\/Symfony\/Component\/Cache\/Messenger\/EarlyExpirationHandler.php\"><code>EarlyExpirationHandler<\/code><\/a> del componente Cache de Symfony implementa la interfaz <a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/191cb52\/src\/Symfony\/Component\/Messenger\/Handler\/MessageHandlerInterface.php\"><code>MessageHandlerInterface<\/code><\/a> del componente Messenger:<\/p>\n<pre><code class=\"language-php\">class EarlyExpirationHandler implements MessageHandlerInterface\n{\n    \/\/...\n}<\/code><\/pre>\n<p>Sin embargo, <code>symfony\/cache<\/code> declara que <code>symfony\/messenger<\/code> es una <a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/6fe82d8\/src\/Symfony\/Component\/Cache\/composer.json#L43\">dependencia para el desarrollo<\/a>. Entonces, al ejecutar Rector en un proyecto que depende de <code>symfony\/cache<\/code>, arrojar\u00e1 un error:<\/p>\n<pre><code>[ERROR] Could not process \"vendor\/symfony\/cache\/Messenger\/EarlyExpirationHandler.php\" file, due to:             \n  \"Analyze error: \"Class Symfony\\Component\\Messenger\\Handler\\MessageHandlerInterface not found.\". Include your files in \"$parameters-&gt;set(Option::AUTOLOAD_PATHS, [...]);\" in \"rector.php\" config.\n  See https:\/\/github.com\/rectorphp\/rector#configuration\".   \n<\/code><\/pre>\n<p>Hay tres soluciones para esta cuesti\u00f3n:<\/p>\n<ol>\n<li>En la configuraci\u00f3n del rector, omita el procesamiento del archivo que hace referencia a ese fragmento de c\u00f3digo:<\/li>\n<\/ol>\n<pre><code class=\"language-php\">return static function (ContainerConfigurator $containerConfigurator): void {\n  \/\/ ...\n\n  $parameters-&gt;set(Option::SKIP, [\n    __DIR__ . '\/vendor\/symfony\/cache\/Messenger\/EarlyExpirationHandler.php',\n  ]);\n};\n<\/code><\/pre>\n<ol start=\"2\">\n<li>Descarga la biblioteca que falta y a\u00f1ada tu ruta para que sea autocargada por Rector:<\/li>\n<\/ol>\n<pre><code class=\"language-php\">return static function (ContainerConfigurator $containerConfigurator): void {\n  \/\/ ...\n\n  $parameters-&gt;set(Option::AUTOLOAD_PATHS, [\n    __DIR__ . '\/vendor\/symfony\/messenger',\n  ]);\n};\n<\/code><\/pre>\n<ol start=\"3\">\n<li>Haz que tu proyecto dependa de la biblioteca que falta para la producci\u00f3n:<\/li>\n<\/ol>\n<pre><code class=\"language-bash\">composer require symfony\/messenger\n<\/code><\/pre>\n<h2>Transpilaci\u00f3n e integraci\u00f3n continua<\/h2>\n<p>Como se mencion\u00f3 anteriormente, en nuestros equipos de desarrollo debemos utilizar la bandera <code>--dry-run<\/code> cuando ejecutamos Rector, o de lo contrario, el c\u00f3digo fuente ser\u00e1 anulado con el c\u00f3digo transpilado. Por esta raz\u00f3n, es m\u00e1s adecuado ejecutar el proceso de transpilaci\u00f3n real durante la integraci\u00f3n continua (CI), donde podemos girar corredores temporales para ejecutar el proceso.<\/p>\n<p>Un momento ideal para ejecutar el proceso de transpilaci\u00f3n es cuando se genera la liberaci\u00f3n de nuestro proyecto. Por ejemplo, el <a href=\"https:\/\/github.com\/GraphQLAPI\/graphql-api-for-wp\/blob\/3f9fd52ddf318b9fbb33653de8acfec8dc4b4665\/.github\/workflows\/main.yml\">c\u00f3digo siguiente<\/a> es un flujo de trabajo para <a href=\"https:\/\/github.com\/features\/actions\">GitHub Actions<\/a>, que crea la liberaci\u00f3n de un plugin de WordPress:<\/p>\n<pre><code class=\"language-yml\">name: Generate Installable Plugin and Upload as Release Asset\non:\n  release:\n    types: [published]\njobs:\n  build:\n    name: Build, Downgrade and Upload Release\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions\/checkout@v2\n      - name: Downgrade code for production (to PHP 7.1)\n        run: |\n          composer install\n          vendor\/bin\/rector process\n          sed -i 's\/Requires PHP: 7.4\/Requires PHP: 7.1\/' graphql-api.php\n      - name: Build project for production\n        run: |\n          composer install --no-dev --optimize-autoloader\n          mkdir build\n      - name: Create artifact\n        uses: montudor\/action-zip@v0.1.0\n        with:\n          args: zip -X -r build\/graphql-api.zip . -x *.git* node_modules\/\\* .* \"*\/\\.*\" CODE_OF_CONDUCT.md CONTRIBUTING.md ISSUE_TEMPLATE.md PULL_REQUEST_TEMPLATE.md rector.php *.dist composer.* dev-helpers** build**\n      - name: Upload artifact\n        uses: actions\/upload-artifact@v2\n        with:\n            name: graphql-api\n            path: build\/graphql-api.zip\n      - name: Upload to release\n        uses: JasonEtco\/upload-to-release@master\n        with:\n          args: build\/graphql-api.zip application\/zip\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n<\/code><\/pre>\n<p>Este flujo de trabajo contiene un procedimiento est\u00e1ndar para <a href=\"https:\/\/leoloso.com\/posts\/github-action-to-release-wp-plugin\/\">liberar un plugin de WordPressa trav\u00e9s de GitHub Actions<\/a>. La nueva adici\u00f3n, para transpilar el c\u00f3digo del plugin de PHP 7.4 a 7.1, ocurre en este paso:<\/p>\n<pre><code class=\"language-yml\">      - name: Downgrade code for production (to PHP 7.1)\n        run: |\n          vendor\/bin\/rector process\n          sed -i 's\/Requires PHP: 7.4\/Requires PHP: 7.1\/' graphql-api.php\n<\/code><\/pre>\n<p>En conjunto, este flujo de trabajo realiza ahora los siguientes pasos:<\/p>\n<ol>\n<li>Comprueba el c\u00f3digo fuente de un plugin de WordPress desde su repositorio, escrito con PHP 7.4<\/li>\n<li>Instala sus dependencias de Composer<\/li>\n<li>Transpila su c\u00f3digo de PHP 7.4 a 7.1<\/li>\n<li>Modifica la entrada \u00abRequiere PHP\u00bb en la cabecera del archivo principal del plugin de <code>\"7.4\"<\/code> a <code>\"7.1\"<\/code><\/li>\n<li>Elimina las dependencias necesarias para el desarrollo<\/li>\n<li>Crea el archivo . zip del plugin, excluyendo todos los archivos innecesarios<\/li>\n<li>Sube el archivo . zip como un activo de lanzamiento (y, adem\u00e1s, como un artefacto a la Acci\u00f3n GitHub)<\/li>\n<\/ol>\n<h2>Prueba del c\u00f3digo transpilado<\/h2>\n<p>Una vez que el c\u00f3digo ha sido transpilado a PHP 7.1, \u00bfc\u00f3mo sabemos que funciona bien? O, en otras palabras, \u00bfc\u00f3mo sabemos que se ha convertido a fondo, y que no se han dejado restos de versiones superiores de c\u00f3digo PHP?<\/p>\n<p>De forma similar a transpilar el c\u00f3digo, podemos implementar la soluci\u00f3n dentro de un proceso de CI. La idea es configurar el entorno del corredor con PHP 7.1 y ejecutar un linter en el c\u00f3digo transpilado. Si alguna parte del c\u00f3digo no es compatible con PHP 7.1 (como una propiedad tipada de PHP 7.4 que no fue convertida), entonces el linter arrojar\u00e1 un error.<\/p>\n<p>Un linter para PHP que funciona bien es <a href=\"https:\/\/github.com\/php-parallel-lint\/PHP-Parallel-Lint\">PHP Parallel Lint<\/a>. Podemos instalar esta biblioteca como una dependencia para el desarrollo en nuestro proyecto, o hacer que el proceso de CI lo instale como un proyecto independiente de Composer:<\/p>\n<pre><code class=\"language-bash\">composer create-project php-parallel-lint\/php-parallel-lint\n<\/code><\/pre>\n<p>Siempre que el c\u00f3digo contenga PHP 7.2 y superior, PHP Parallel Lint lanzar\u00e1 un error como <a href=\"https:\/\/github.com\/leoloso\/PoP\/runs\/2751846434?check_suite_focus=true\">\u00e9ste<\/a>:<\/p>\n<pre><code>Run php-parallel-lint\/parallel-lint layers\/ vendor\/ --exclude vendor\/symfony\/polyfill-ctype\/bootstrap80.php --exclude vendor\/symfony\/polyfill-intl-grapheme\/bootstrap80.php --exclude vendor\/symfony\/polyfill-intl-idn\/bootstrap80.php --exclude vendor\/symfony\/polyfill-intl-normalizer\/bootstrap80.php --exclude vendor\/symfony\/polyfill-mbstring\/bootstrap80.php\nPHP 7.1.33 | 10 parallel jobs\n............................................................   60\/2870 (2 %)\n............................................................  120\/2870 (4 %)\n...\n............................................................  660\/2870 (22 %)\n.............X..............................................  720\/2870 (25 %)\n............................................................  780\/2870 (27 %)\n...\n............................................................ 2820\/2870 (98 %)\n..................................................           2870\/2870 (100 %)\n\n\nChecked 2870 files in 15.4 seconds\nSyntax error found in 1 file\n\n------------------------------------------------------------\nParse error: layers\/GraphQLAPIForWP\/plugins\/graphql-api-for-wp\/graphql-api.php:55\n    53|     '0.8.0',\n    54|     \\__('GraphQL API for WordPress', 'graphql-api'),\n  &gt; 55| ))) {\n    56|     $plugin-&gt;setup();\n    57| }\nUnexpected ')' in layers\/GraphQLAPIForWP\/plugins\/graphql-api-for-wp\/graphql-api.php on line 55\nError: Process completed with exit code 1.\n<\/code><\/pre>\n<p>Vamos a a\u00f1adir el linter en el flujo de trabajo de nuestro CI. Los pasos a ejecutar para transpilar c\u00f3digo de PHP 8.0 a 7.1 y probarlo son:<\/p>\n<ol>\n<li>Consulta el c\u00f3digo fuente<\/li>\n<li>Hacer que el entorno ejecute PHP 8.0, para que Rector pueda interpretar el c\u00f3digo fuente<\/li>\n<li>Transpila el c\u00f3digo a PHP 7.1<\/li>\n<li>Instala la herramienta PHP linter<\/li>\n<li>Cambia la versi\u00f3n de PHP del entorno a la 7.1<\/li>\n<li>Ejecuta el linter en el c\u00f3digo transpilado<\/li>\n<\/ol>\n<p>Este <a href=\"https:\/\/github.com\/leoloso\/PoP\/blob\/b93d3e35cb59e0281b45899fd82231c3d8cbbe25\/.github\/workflows\/downgrade_php_tests.yml\">flujo de trabajo de GitHubAction<\/a> hace el trabajo:<\/p>\n<pre><code class=\"language-yaml\">name: Downgrade PHP tests\njobs:\n  main:\n    name: Downgrade code to PHP 7.1 via Rector, and execute tests\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions\/checkout@v2\n\n      - name: Set-up PHP\n        uses: shivammathur\/setup-php@v2\n        with:\n          php-version: 8.0\n          coverage: none\n\n      - name: Local packages - Downgrade PHP code via Rector\n        run: |\n          composer install\n          vendor\/bin\/rector process\n\n      # Prepare for testing on PHP 7.1\n      - name: Install PHP Parallel Lint\n        run: composer create-project php-parallel-lint\/php-parallel-lint --ansi\n\n      - name: Switch to PHP 7.1\n        uses: shivammathur\/setup-php@v2\n        with:\n          php-version: 7.1\n          coverage: none\n\n      # Lint the transpiled code\n      - name: Run PHP Parallel Lint on PHP 7.1\n        run: php-parallel-lint\/parallel-lint src\/ vendor\/ --exclude vendor\/symfony\/polyfill-ctype\/bootstrap80.php --exclude vendor\/symfony\/polyfill-intl-grapheme\/bootstrap80.php --exclude vendor\/symfony\/polyfill-intl-idn\/bootstrap80.php --exclude vendor\/symfony\/polyfill-intl-normalizer\/bootstrap80.php --exclude vendor\/symfony\/polyfill-mbstring\/bootstrap80.php\n<\/code><\/pre>\n<p>Ten en cuenta que varios archivos <code>bootstrap80.php<\/code> de las bibliotecas polyfill de Symfony (que no necesitan ser transpiladas) deben ser excluidos del linter. Estos archivos contienen PHP 8.0, por lo que el linter arrojar\u00eda errores al procesarlos. Sin embargo, excluir estos archivos es seguro ya que se cargar\u00e1n en producci\u00f3n s\u00f3lo cuando <a href=\"https:\/\/github.com\/symfony\/polyfill-mbstring\/blob\/9ad2f3c\/bootstrap.php#L14-L16\">se ejecute PHP 8.0 o superior<\/a>:<\/p>\n<pre><code class=\"language-php\">if (\\PHP_VERSION_ID &gt;= 80000) {\n  return require __DIR__.'\/bootstrap80.php';\n}\n<\/code><\/pre>\n\n<h2>Resumen<\/h2>\n<p>Este art\u00edculo nos ense\u00f1\u00f3 c\u00f3mo transpilar nuestro c\u00f3digo PHP, permiti\u00e9ndonos usar PHP 8.0 en el c\u00f3digo fuente y crear una versi\u00f3n que funciona en PHP 7.1. La transpilaci\u00f3n se realiza a trav\u00e9s de <a href=\"https:\/\/github.com\/rectorphp\/rector\">Rector<\/a>, una herramienta de reconstrucci\u00f3n de PHP.<\/p>\n<p>Transpilar nuestro c\u00f3digo nos hace mejores desarrolladores, ya que podemos detectar mejor los errores en el desarrollo y producir un c\u00f3digo que, naturalmente, es m\u00e1s f\u00e1cil de leer y entender.<\/p>\n<p>La transpilaci\u00f3n tambi\u00e9n nos permite desacoplar nuestro c\u00f3digo con requisitos espec\u00edficos de PHP del CMS. Ahora podemos hacerlo si deseamos utilizar la \u00faltima versi\u00f3n de PHP para crear un plugin de WordPress o un m\u00f3dulo de Drupal de acceso p\u00fablico sin restringir gravemente nuestra base de usuarios.<\/p>\n<p><em>\u00bfTe queda alguna duda sobre la transpilaci\u00f3n de PHP? \u00a1H\u00e1znoslo saber en la secci\u00f3n de comentarios!<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>En circunstancias ideales, deber\u00edamos utilizar PHP 8.0 (la \u00faltima versi\u00f3n en el momento de escribir este art\u00edculo) para todos nuestros sitios y actualizarlo tan pronto como &#8230;<\/p>\n","protected":false},"author":196,"featured_media":44038,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[432,666],"topic":[1304,1316],"class_list":["post-44032","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-php","tag-programming","topic-aprender-php","topic-funcion-php"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v24.6 (Yoast SEO v24.6) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>La Gu\u00eda Definitiva para Transpilar C\u00f3digo PHP<\/title>\n<meta name=\"description\" content=\"\u00bfCansado de trabajar con m\u00faltiples versiones de PHP? Aprende a escribir el c\u00f3digo fuente con PHP 8.0, y transp\u00edlalo a versiones anteriores de PHP.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"La Gu\u00eda Definitiva para Transpilar C\u00f3digo PHP\" \/>\n<meta property=\"og:description\" content=\"\u00bfCansado de trabajar con m\u00faltiples versiones de PHP? Aprende a escribir el c\u00f3digo fuente con PHP 8.0, y transp\u00edlalo a versiones anteriores de PHP.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/\" \/>\n<meta property=\"og:site_name\" content=\"Kinsta\u00ae\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/kinsta.es\/\" \/>\n<meta property=\"article:published_time\" content=\"2021-09-24T06:37:16+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-08-23T15:28:04+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2021\/09\/transpiling-php.jpeg\" \/>\n\t<meta property=\"og:image:width\" content=\"1460\" \/>\n\t<meta property=\"og:image:height\" content=\"730\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Leonardo Losoviz\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:description\" content=\"\u00bfCansado de trabajar con m\u00faltiples versiones de PHP? Aprende a escribir el c\u00f3digo fuente con PHP 8.0, y transp\u00edlalo a versiones anteriores de PHP.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2021\/09\/transpiling-php.jpeg\" \/>\n<meta name=\"twitter:creator\" content=\"@losoviz\" \/>\n<meta name=\"twitter:site\" content=\"@Kinsta_ES\" \/>\n<meta name=\"twitter:label1\" content=\"Escrito por\" \/>\n\t<meta name=\"twitter:data1\" content=\"Leonardo Losoviz\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tiempo de lectura\" \/>\n\t<meta name=\"twitter:data2\" content=\"23 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/\"},\"author\":{\"name\":\"Leonardo Losoviz\",\"@id\":\"https:\/\/kinsta.com\/es\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\"},\"headline\":\"La Gu\u00eda Definitiva para Transpilar C\u00f3digo PHP\",\"datePublished\":\"2021-09-24T06:37:16+00:00\",\"dateModified\":\"2023-08-23T15:28:04+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/\"},\"wordCount\":4390,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/es\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2021\/09\/transpiling-php.jpeg\",\"keywords\":[\"php\",\"programming\"],\"articleSection\":[\"Sin Categor\u00eda\"],\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/\",\"url\":\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/\",\"name\":\"La Gu\u00eda Definitiva para Transpilar C\u00f3digo PHP\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/es\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2021\/09\/transpiling-php.jpeg\",\"datePublished\":\"2021-09-24T06:37:16+00:00\",\"dateModified\":\"2023-08-23T15:28:04+00:00\",\"description\":\"\u00bfCansado de trabajar con m\u00faltiples versiones de PHP? Aprende a escribir el c\u00f3digo fuente con PHP 8.0, y transp\u00edlalo a versiones anteriores de PHP.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#primaryimage\",\"url\":\"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2021\/09\/transpiling-php.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2021\/09\/transpiling-php.jpeg\",\"width\":1460,\"height\":730,\"caption\":\"La gu\u00eda definitiva para transpilar c\u00f3digo PHP\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinsta.com\/es\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Funci\u00f3n PHP\",\"item\":\"https:\/\/kinsta.com\/es\/secciones\/funcion-php\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"La Gu\u00eda Definitiva para Transpilar C\u00f3digo PHP\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinsta.com\/es\/#website\",\"url\":\"https:\/\/kinsta.com\/es\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Soluciones de alojamiento premium, r\u00e1pidas y seguras\",\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/es\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinsta.com\/es\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinsta.com\/es\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinsta.com\/es\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/kinsta.com\/es\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/es\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/kinsta.es\/\",\"https:\/\/x.com\/Kinsta_ES\",\"https:\/\/www.instagram.com\/kinstahosting\/\",\"https:\/\/www.linkedin.com\/company\/kinsta\/\",\"https:\/\/www.pinterest.com\/kinstahosting\/\",\"https:\/\/www.youtube.com\/c\/Kinsta\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/kinsta.com\/es\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\",\"name\":\"Leonardo Losoviz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/kinsta.com\/es\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g\",\"caption\":\"Leonardo Losoviz\"},\"description\":\"Leo writes about innovative web development trends, mostly concerning PHP, WordPress and GraphQL. You can find him at leoloso.com and twitter.com\/losoviz.\",\"sameAs\":[\"https:\/\/leoloso.com\",\"https:\/\/x.com\/losoviz\",\"https:\/\/www.youtube.com\/@GatoGraphQL\"],\"url\":\"https:\/\/kinsta.com\/es\/blog\/author\/leonardolosoviz\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"La Gu\u00eda Definitiva para Transpilar C\u00f3digo PHP","description":"\u00bfCansado de trabajar con m\u00faltiples versiones de PHP? Aprende a escribir el c\u00f3digo fuente con PHP 8.0, y transp\u00edlalo a versiones anteriores de PHP.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/","og_locale":"es_ES","og_type":"article","og_title":"La Gu\u00eda Definitiva para Transpilar C\u00f3digo PHP","og_description":"\u00bfCansado de trabajar con m\u00faltiples versiones de PHP? Aprende a escribir el c\u00f3digo fuente con PHP 8.0, y transp\u00edlalo a versiones anteriores de PHP.","og_url":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/kinsta.es\/","article_published_time":"2021-09-24T06:37:16+00:00","article_modified_time":"2023-08-23T15:28:04+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2021\/09\/transpiling-php.jpeg","type":"image\/jpeg"}],"author":"Leonardo Losoviz","twitter_card":"summary_large_image","twitter_description":"\u00bfCansado de trabajar con m\u00faltiples versiones de PHP? Aprende a escribir el c\u00f3digo fuente con PHP 8.0, y transp\u00edlalo a versiones anteriores de PHP.","twitter_image":"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2021\/09\/transpiling-php.jpeg","twitter_creator":"@losoviz","twitter_site":"@Kinsta_ES","twitter_misc":{"Escrito por":"Leonardo Losoviz","Tiempo de lectura":"23 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#article","isPartOf":{"@id":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/"},"author":{"name":"Leonardo Losoviz","@id":"https:\/\/kinsta.com\/es\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238"},"headline":"La Gu\u00eda Definitiva para Transpilar C\u00f3digo PHP","datePublished":"2021-09-24T06:37:16+00:00","dateModified":"2023-08-23T15:28:04+00:00","mainEntityOfPage":{"@id":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/"},"wordCount":4390,"commentCount":0,"publisher":{"@id":"https:\/\/kinsta.com\/es\/#organization"},"image":{"@id":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2021\/09\/transpiling-php.jpeg","keywords":["php","programming"],"articleSection":["Sin Categor\u00eda"],"inLanguage":"es","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/","url":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/","name":"La Gu\u00eda Definitiva para Transpilar C\u00f3digo PHP","isPartOf":{"@id":"https:\/\/kinsta.com\/es\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#primaryimage"},"image":{"@id":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2021\/09\/transpiling-php.jpeg","datePublished":"2021-09-24T06:37:16+00:00","dateModified":"2023-08-23T15:28:04+00:00","description":"\u00bfCansado de trabajar con m\u00faltiples versiones de PHP? Aprende a escribir el c\u00f3digo fuente con PHP 8.0, y transp\u00edlalo a versiones anteriores de PHP.","breadcrumb":{"@id":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/"]}]},{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#primaryimage","url":"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2021\/09\/transpiling-php.jpeg","contentUrl":"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2021\/09\/transpiling-php.jpeg","width":1460,"height":730,"caption":"La gu\u00eda definitiva para transpilar c\u00f3digo PHP"},{"@type":"BreadcrumbList","@id":"https:\/\/kinsta.com\/es\/blog\/transpilacion-php\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinsta.com\/es\/"},{"@type":"ListItem","position":2,"name":"Funci\u00f3n PHP","item":"https:\/\/kinsta.com\/es\/secciones\/funcion-php\/"},{"@type":"ListItem","position":3,"name":"La Gu\u00eda Definitiva para Transpilar C\u00f3digo PHP"}]},{"@type":"WebSite","@id":"https:\/\/kinsta.com\/es\/#website","url":"https:\/\/kinsta.com\/es\/","name":"Kinsta\u00ae","description":"Soluciones de alojamiento premium, r\u00e1pidas y seguras","publisher":{"@id":"https:\/\/kinsta.com\/es\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinsta.com\/es\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Organization","@id":"https:\/\/kinsta.com\/es\/#organization","name":"Kinsta","url":"https:\/\/kinsta.com\/es\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/kinsta.com\/es\/#\/schema\/logo\/image\/","url":"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinsta.com\/es\/wp-content\/uploads\/sites\/8\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinsta.com\/es\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/kinsta.es\/","https:\/\/x.com\/Kinsta_ES","https:\/\/www.instagram.com\/kinstahosting\/","https:\/\/www.linkedin.com\/company\/kinsta\/","https:\/\/www.pinterest.com\/kinstahosting\/","https:\/\/www.youtube.com\/c\/Kinsta"]},{"@type":"Person","@id":"https:\/\/kinsta.com\/es\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238","name":"Leonardo Losoviz","image":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/kinsta.com\/es\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g","caption":"Leonardo Losoviz"},"description":"Leo writes about innovative web development trends, mostly concerning PHP, WordPress and GraphQL. You can find him at leoloso.com and twitter.com\/losoviz.","sameAs":["https:\/\/leoloso.com","https:\/\/x.com\/losoviz","https:\/\/www.youtube.com\/@GatoGraphQL"],"url":"https:\/\/kinsta.com\/es\/blog\/author\/leonardolosoviz\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinsta.com\/es\/wp-json\/wp\/v2\/posts\/44032","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinsta.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinsta.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinsta.com\/es\/wp-json\/wp\/v2\/users\/196"}],"replies":[{"embeddable":true,"href":"https:\/\/kinsta.com\/es\/wp-json\/wp\/v2\/comments?post=44032"}],"version-history":[{"count":15,"href":"https:\/\/kinsta.com\/es\/wp-json\/wp\/v2\/posts\/44032\/revisions"}],"predecessor-version":[{"id":44297,"href":"https:\/\/kinsta.com\/es\/wp-json\/wp\/v2\/posts\/44032\/revisions\/44297"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinsta.com\/es\/wp-json\/kinsta\/v1\/posts\/44032\/translations\/en"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinsta.com\/es\/wp-json\/kinsta\/v1\/posts\/44032\/translations\/es"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinsta.com\/es\/wp-json\/kinsta\/v1\/posts\/44032\/translations\/it"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinsta.com\/es\/wp-json\/kinsta\/v1\/posts\/44032\/translations\/fr"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinsta.com\/es\/wp-json\/kinsta\/v1\/posts\/44032\/translations\/pt"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinsta.com\/es\/wp-json\/kinsta\/v1\/posts\/44032\/translations\/de"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinsta.com\/es\/wp-json\/kinsta\/v1\/posts\/44032\/translations\/nl"},{"href":"https:\/\/kinsta.com\/es\/wp-json\/kinsta\/v1\/posts\/44032\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinsta.com\/es\/wp-json\/wp\/v2\/media\/44038"}],"wp:attachment":[{"href":"https:\/\/kinsta.com\/es\/wp-json\/wp\/v2\/media?parent=44032"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinsta.com\/es\/wp-json\/wp\/v2\/tags?post=44032"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinsta.com\/es\/wp-json\/wp\/v2\/topic?post=44032"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}