PHP

Última revisión: 28 de diciembre de 2021

Lenguaje de programación PHP

PHP es el lenguaje de programación principal sobre el que funciona WordPress. Al ser el sistema principal de cálculo, debe estar optimizado para que el tiempo de carga del sitio sea el menor posible.

¿Qué versión utilizar?

Hay que tener en cuenta que cada versión mayor de WordPress es compatible con las versiones que había en la época en la que se desarrolló, pero que no tiene porqué dar soporte con versiones nuevas.

Podemos ver la compatibilidad en el PHP matrix de WordPress.

Hay que tener en cuenta 2 factores para decidir qué versión de PHP instalar en tu sitio. La primera es saber qué versiones de PHP soporta WordPress en ese momento.

Si tomamos como ejemplo WordPress 5.8, en su documentación nos dice que funciona con versiones entre PHP 5.6.20 y PHP 8.0. También sabemos que oficialmente no es 100% compatible con PHP 8.0, aunque se le da un soporte para controlar posibles errores. En su fecha de lanzamiento existían PHP 7.3, 7.4 y 8.0. Teniendo esto en cuenta, la opción lógica para elegir versión de PHP sería la 7.4.

Con este sistema deberíamos siempre tener en cuenta que cada versión mayor de WordPress funciona bien con una versión de PHP, pero que con el paso del tiempo, cada 3 años esa versión de PHP dejará de tener soporte, por lo que deberemos ir manteniendo las versiones de PHP mayores al día con las versiones de PHP.

PHP: CGI vs. FPM

A la hora de configurar PHP para trabajar con el servidor web existen, principalmente, dos formas de hacerlo: en Modo CGI y en Modo FPM.

Una forma sencilla de entender cuál es el funcionamiento de estos dos modos es que el sistema CGI funciona como un programa ejecutable, de forma que el servidor web, cuando recibe un código PHP, llama a ese programa y ejecuta el código, y que el sistema CGI funciona como un servicio, de forma que siempre está en memoria como algo independiente, y cuando se recibe una petición al servidor web, este llama al servicio que está a la escucha y pendiente de ser ejecutado.

Cada uno de estos sistemas tiene sus pros y contras y cada servidor web tiene sus preferencias de configuración. Por norma general nos encontramos que Apache HTTPD suele venir integrado para funcionar en modo CGI, y que nginx viene mejor preparado para FPM.

Si lo que buscamos es mejorar el rendimiento y poder configurar de una forma concreta el propio PHP, la opción ideal será la de usar PHP en modo FPM, para así poder asignar recursos de una forma más detallada a cada usuario o sitio web.

Extensiones de PHP

Aunque para que funcione WordPress en sí mismo son necesarias unas mínimas extensiones, la mayoría de plugins requieren algunas extensiones extra que no vienen por defecto en muchas instalaciones.

Por ejemplo, en Ubuntu podríamos hacer una instalación de PHP 8.0 con las siguientes extensiones para tener una cobertura bastante amplia.

add-apt-repository -y -s ppa:ondrej/php
apt-get -y install php8.0 php8.0-{fpm,common,dev,cli,bcmath,curl,gd,intl,imap,mbstring,mysql,opcache,readline,soap,xml,zip,imagick} imagemagick libmagickwand-dev

Posteriormente, pondríamos por defecto esta versión de PHP.

update-alternatives --set php /usr/bin/php8.0
update-alternatives --set php-config /usr/bin/php-config8.0
update-alternatives --set phpize /usr/bin/phpize8.0
update-alternatives --set phar /usr/bin/phar8.0
update-alternatives --set phar.phar /usr/bin/phar.phar8.0

Módulos PHP en Salud del Sitio

Dentro de las Herramientas de WordPress podemos encontrar el Salud del Sitio que nos indicará si alguna extensión de PHP (entre otras cosas) no está instalada o configurada correctamente. Para solucionar los problemas que puedan sugir, puedes revisar esta documentación:

Ficheros de configuración de PHP

Si tomamos de base un PHP 8.0, podríamos tener 3 ficheros de configuración base. El primero de ellos es el de PHP, general (php.ini); el segundo la configuración general del PHP-FPM; finalmente, la configuración del pool que estará escuchando.

php.ini

Es importante tener en cuenta que esta configuración puede variar dependiendo de cada sistema operativo e instalación. En este caso destacamos los siguientes parámetros.

Configuración general

max_execution_time = 300
max_input_time = 300
memory_limit = 256M
max_input_vars = 1500
error_reporting = E_ALL
display_errors = Off
log_errors = On
post_max_size = 128M
upload_max_filesize = 128M

A destacar que los tiempos de ejecución deben ir en la misma línea que otros servicios, como por ejemplo el servidor web. No tiene sentido que el servidor web tenga un límite de 30 segundos y PHP de 60 segundos, porque en algún momento generará errores. Ambos deberían ir a la par.

Por otro lado, es interesante hacer logs de PHP para encontrar posibles errores que se guarden en el sistema, independientemente de la activación o no del modo DEBUG de WordPress.

Finalmente, configuraremos el tamaño máximo de los ficheros de subida al Media. Estos 128 MB también deben ir en línea del tamaño de datos que acepta el propio servidor web, que también debería ser configurado con el mismo tamaño.

PHP OPcache

Uno de los elementos más interesantes a la hora de optimizar PHP es el uso de algún sistema de caché propio. En este caso podemos activar el PHP OPcache de forma sencilla desde la configuración e indicar la cantidad de megabytes que vamos a darle de memoria para cachear el código fuente, que en este caso es de 128MB y debería ser suficiente para una instalación de WordPress. Si la caché de WordPress es grande, se puede ampliar a 256 o 512.

[opcache]
opcache.enable=1
opcache.memory_consumption=128

Configuración completa

[PHP]
engine = On
short_open_tag = Off
precision = 14
output_buffering = 4096
zlib.output_compression = Off
implicit_flush = Off
unserialize_callback_func =
serialize_precision = -1
disable_functions = 
disable_classes =
zend.enable_gc = On
zend.exception_ignore_args = On
zend.exception_string_param_max_len = 0
expose_php = Off
max_execution_time = 300
max_input_time = 300
memory_limit = 256M
max_input_vars = 1500
error_reporting = E_ALL
display_errors = Off
display_startup_errors = Off
log_errors = On
log_errors_max_len = 1024
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
html_errors = On
variables_order = "GPCS"
request_order = "GP"
register_argc_argv = Off
auto_globals_jit = On
post_max_size = 128M
auto_prepend_file =
auto_append_file =
default_mimetype = "text/html"
default_charset = "UTF-8"
doc_root =
user_dir =
enable_dl = Off
file_uploads = On
upload_max_filesize = 128M
max_file_uploads = 20
allow_url_fopen = On
allow_url_include = Off
default_socket_timeout = 300
[CLI Server]
cli_server.color = On
[Date]
date.timezone = 'UTC'
[Pdo_mysql]
pdo_mysql.cache_size = 2000
pdo_mysql.default_socket=
[mail function]
SMTP = localhost
smtp_port = 25
mail.add_x_header = Off
[ODBC]
odbc.allow_persistent = On
odbc.check_persistent = On
odbc.max_persistent = -1
odbc.max_links = -1
odbc.defaultlrl = 4096
odbc.defaultbinmode = 1
[MySQLi]
mysqli.max_persistent = -1
mysqli.allow_persistent = On
mysqli.max_links = -1
mysqli.cache_size = 2000
mysqli.default_port = 3306
mysqli.default_socket =
mysqli.default_host =
mysqli.default_user =
mysqli.default_pw =
mysqli.reconnect = Off
[mysqlnd]
mysqlnd.collect_statistics = On
mysqlnd.collect_memory_statistics = Off
[bcmath]
bcmath.scale = 0
[Session]
session.save_handler = files
session.use_strict_mode = 0
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.cookie_httponly =
session.serialize_handler = php
session.gc_probability = 0
session.gc_divisor = 1000
session.gc_maxlifetime = 1440
session.referer_check =
session.cache_limiter = nocache
session.cache_expire = 180
session.use_trans_sid = 0
session.sid_length = 26
session.trans_sid_tags = "a=href,area=href,frame=src,form="
session.sid_bits_per_character = 5
[Assertion]
zend.assertions = -1
[Tidy]
tidy.clean_output = Off
[soap]
soap.wsdl_cache_enabled=1
soap.wsdl_cache_dir="/tmp"
soap.wsdl_cache_ttl=86400
soap.wsdl_cache_limit = 5
[ldap]
ldap.max_links = -1
[opcache]
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10240
opcache.max_wasted_percentage=15
opcache.revalidate_freq=60
opcache.fast_shutdown=0
opcache.enable_cli=1
opcache.validate_timestamps=0

php-fpm.conf

Si utilizamos PHP en modo FPM, deberemos tener una configuración global para este sistema. Esta configuración puede ser similar a la siguiente. Este fichero incluirá posteriormente las configuraciones personalizadas del pool de cuentas.

[global]
pid = /run/php/php8.0-fpm.pid
error_log = /var/log/php8.0-fpm.log
syslog.facility = daemon
syslog.ident = php-fpm
log_level = notice
log_limit = 4096
process.max = 2048
daemonize = yes
rlimit_files = 65535
events.mechanism = epoll
emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 10s
include=/etc/php/8.0/fpm/pool.d/*.conf

pool.d

Por norma general el sistema incluye un usuario general para el pool de usuarios que pueden ejecutarse desde el servidor web.

Existen muchas formas de configurar el pool. Lo mejor es tener un usuario específico para cada grupo de webs o cuentas dentro del sistema, que permitan limitar o restringir el tamaño máximo y los picos de memoria en cada uno de los casos.

La idea será dejar el pool por defecto en mínimos, y posteriormente adaptar cada usuario según los recursos que disponga el servidor.

www.conf

El fichero por defecto lo dejaremos con el usuario del sistema (en este caso el de nginx) con solo un hijo procesado como máximo. En teoría este proceso no se utilizará.

[www]
user = www-data
group = www-data
listen = /var/run/php/php8.0-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.allowed_clients = 127.0.0.1
pm = static
pm.max_children = 1
pm.max_requests = 1024
pm.process_idle_timeout = 5s
request_slowlog_timeout = 30s
slowlog = /var/log/nginx/php-www.log.slow

usuario.conf

Dependiendo del tamaño del servidor y de los recursos que queramos darle al sistema, una mezcla entre la cantidad de CPU y de memoria RAM disponible, configuraremos el tamaño máximo de uso del pool para un usuario concreto.

PHP-FPM se puede configurar de 3 maneras: static, dymanic y ondemand. Si vas a utilizar una máquina de forma exclusiva en la que sepas los recursos que va a utilizar PHP; puedes activar el sistema de forma estática (static), ya que tendrás una configuración reservada y específica de CPU y RAM para PHP de la que podrás disponer siempre. Los casos dinámicos y bajo demanda son variables y en general permiten cierta flexibilidad. Por ejemplo en el caso del dinámico podrás indicar cuantos hijos cargar con un mínimo y un máximo, quedando siempre ese mínimo activo y disponible (y consumiendo memoria), o el sistema bajo demanda en el que pones límites por arriba con un máximo de hijos y de peticiones.

En el ejemplo, además, vamos a indicar el número de versión de PHP (80 haciendo referencia a 8.0) para que al ver la lista de procesos sepamos qué versión de PHP está ejecutándose.

[usuario80]
user = usuario
group = usuario
listen = /var/run/php/usuario80.sock
listen.mode = 0660
listen.owner = www-data
listen.group = www-data
listen.allowed_clients = 127.0.0.1
chdir = /webs/usuario
pm = ondemand
pm.max_children = 24
pm.max_requests = 1024
pm.process_idle_timeout = 5s
request_slowlog_timeout = 30s
slowlog = /var/log/nginx/php-usuario80.log.slow

Límite de memoria

El límite de memoria utilizado por PHP se puede configurar en las directivas del php.ini. Aun así, WordPress permite desde el fichero de configuración wp-config.php establecer los límites. De esta forma si los del servidor son mayores se ejecutarán esos, pero si los que establece WordPress son mayores, se usaran esos.

Esta opción declara la cantidad de memoria que WordPress debe solicitar para renderizar el frontal (front-end) del sitio web.

define( 'WP_MEMORY_LIMIT', '128M' );

Como habitualmente el panel de administración (wp-admin) requiere más memoria, hay una configuración separada para la cantidad que se puede establecer para los usuarios que han iniciado sesión. Esto suele ser muy útil en la carga de imágenes (ya que tras la subida suelen ser procesadas). Puedes establecerlo por encima del límite del frontal para asegurar que su panel de administración tiene todos los recursos que necesita.

define( 'WP_MAX_MEMORY_LIMIT', '256M' );

En cualquier caso, recuerda que en ningún caso el WP_MEMORY_LIMIT o el WP_MAX_MEMORY_LIMIT deben superar la configuración del memory_limit en el php.ini.

¿Cuál es el límite de memoria correcto?

En muchas ocasiones, cuando WordPress va lento, se suele aumentar el valor del límite de memoria para que funcione mejor. También ocurre cuando se queda en blanco porque algún plugin consume muchos más recursos de los que corresponde.

En estos casos, sí que es verdad que aumentar el límite de memoria puede solucionar esta situación, pero no es la mejor solución, ya que aumentar el límite de memoria implica que se podrán ejecutar menos procesos.

Por norma general, un proceso de WordPress en una configuración estándar con los plugins más habituales (por ejemplo uno de SEO, de formulario y varios más) hace que el sistema consuma unos 96MB de memoria por ejecución. Es por esto que se recomienda configurar el frontal en unos 128MB, para que, en casos de algún pico, tenga cierto margen. En el panel de administración, al tener que calcular más, por norma general, se puede subir ese límite al doble, teniendo en cuenta que, por norma general, hay menos usuarios navegando de forma concurrente.

Esto significa que si nuestro servidor tiene 2 GB de RAM disponible para PHP, se podrían ejecutar 16 procesos concurrentes (16 * 128 = 2024). Si aumentamos el límite a 1024 en vez de dejarlo a 128, los procesos concurrentes disminuyen hasta un máximo de 4 en casos en los que se esté consumiendo el máximo, lo que haría que con varios usuarios navegando de forma simultánea el sitio se sature.

De esta forma, la configuración recomendada para un sitio normal es:

  • WP_MEMORY_LIMIT: 128 MB
  • WP_MAX_MEMORY_LIMIT: 256 MB

Si tenemos un sitio web con muchos plugins pesados (por ejemplo un WooCommerce con un plugin de SEO como Yoast y algún sistema más) podríamos ampliar la configuración al doble, aunque también deberíamos aumentar los recursos del servidor.

  • WP_MEMORY_LIMIT: 256 MB
  • WP_MAX_MEMORY_LIMIT: 512 MB

Sobrepasar los límites de 256 MB en el frontal y 512 MB en el panel de administración significaría que hay algún plugin o alguna incompatibilidad en algún sitio y que lo mejor sería revisar esos plugins o la configuración general del sitio (e incluso la propia configuración del PHP) para validar que todo está configurado correctamente.