PHPlay

Curso de PHP gratis · Capítulo 3 de 13 · con Nadia Cadena

Strings en PHP: comillas, concatenación, interpolación y UTF-8

Trabajar con texto es lo que más harás en PHP. Verás la diferencia entre comillas simples y dobles, cómo concatenar e interpolar, y por qué strlen("ñ") devuelve 2.

Comillas simples y dobles

Un string se escribe entre comillas, simples o dobles, pero no son iguales:

  • Comillas simples '...': el texto va tal cual. Solo entienden \' (una comilla) y \\ (una barra).
  • Comillas dobles "...": PHP interpreta lo de dentro: secuencias de escape como \n (salto de línea), \t (tabulador), \" o \$... y variables.
Pergamino del porque

¿Por qué dos tipos? Porque a veces quieres texto literal (por ejemplo, un texto con muchos $ o barras) y a veces quieres que PHP rellene huecos. Regla práctica: usa simples cuando el texto no tiene nada que interpretar; dobles cuando sí.

<?php
$dron = "BIT-8";
echo 'Hola $dron\n';
echo "\n";
echo "Hola $dron\n";
echo "Comillas \"dobles\" y un dólar: \$5\n";
echo 'It\'s ', "tab:\t|", "\n";
Salida
Hola $dron\n
Hola BIT-8
Comillas "dobles" y un dólar: $5
It's tab:	|

Interpolación: rellenar huecos

Dentro de comillas dobles, PHP sustituye las variables por su valor. A esto se le llama interpolación.

Si el nombre de la variable va pegado a más letras, PHP no sabe dónde acaba: "$frutas" busca una variable llamada $frutas. Para delimitarla usa llaves: "{$fruta}s".

Las llaves también permiten meter expresiones más complejas, como elementos de un array "{$precios['heno']}" o propiedades de un objeto "{$dron->nombre}" (ya las verás).

Cuidado

Si intentas interpolar una variable que no existe, PHP da un Warning y la sustituye por nada. Fíjate en la última línea del ejemplo.

<?php
$fruta = "manzana";
$cantidad = 3;
echo "Tengo $cantidad {$fruta}s\n";
echo "Pesan {$cantidad}kg\n";
echo "Tengo $cantidad $frutas\n";
Salida
Tengo 3 manzanas
Pesan 3kg
Tengo 3 

Concatenar con el punto

Para unir strings se usa el operador punto . (¡no el +!). Y .= añade al final de una variable.

$saludo = "Hola" . ", " . "mundo";
$saludo .= "!";   // equivale a $saludo = $saludo . "!";
Pergamino del porque

¿Por qué no + como en otros lenguajes? Porque en PHP + es siempre una suma numérica: "3" + "4" da 7. Tener un operador distinto para unir evita ambigüedades: "3" . "4" da "34".

Cuidado

Precedencia en PHP 8: + y - se evalúan antes que el punto. Así, "Total: " . 2 + 3 da "Total: 5". (En PHP 7 era al revés, y daba un resultado muy raro.) Ante la duda, usa paréntesis.

<?php
$saludo = "Hola" . ", " . "mundo";
$saludo .= "!";
echo $saludo, "\n";
echo "3" + "4", " frente a ", "3" . "4", "\n";
echo "Total: " . 2 + 3, "\n";
echo "Total: " . (2 * 3), "\n";
Salida
Hola, mundo!
7 frente a 34
Total: 5
Total: 6
Pregunta

¿Qué imprime echo "10" . "5" + 1; en PHP 8?

  1. 106
  2. 16
  3. 1051
  4. Un error
Ver respuesta

A. 106

En PHP 8 la suma va primero: "5" + 1 = 6, y luego "10" . 6 = "106".

Funciones de cadena

PHP tiene decenas de funciones para strings. Las más útiles:

  • strlen($s): longitud (en bytes, ¡ojo!).
  • strtoupper($s), strtolower($s), ucfirst($s), ucwords($s).
  • str_repeat($s, $n): repetir.
  • str_replace($buscar, $poner, $s): sustituir.
  • strpos($s, $aguja): posición de la primera aparición (o false).
  • str_contains, str_starts_with, str_ends_with (PHP 8).
  • trim($s): quitar espacios de los extremos.
  • substr($s, $inicio, $largo): un trozo.
  • str_pad($s, $largo, $relleno): rellenar hasta un ancho.
Pergamino del porque

Los strings en PHP son valores: estas funciones NO modifican el original, devuelven un string nuevo. Si quieres guardar el cambio, asígnalo: $s = strtoupper($s);.

<?php
$s = "  carta para Rasmus  ";
echo "[", trim($s), "]\n";
echo strtoupper(trim($s)), "\n";
echo str_replace("Rasmus", "Vera", $s), "\n";
echo strpos("hola mundo", "mundo"), "\n";
var_dump(str_contains($s, "carta"));
echo substr("BIT-8 el dron", 0, 5), "\n";
echo str_pad("7", 3, "0", STR_PAD_LEFT), "\n";
echo "[", $s, "]\n";
Salida
[carta para Rasmus]
CARTA PARA RASMUS
  carta para Vera  
5
bool(true)
BIT-8
007
[  carta para Rasmus  ]

Bytes y caracteres: el secreto de la ñ

Aquí va el secreto: para PHP un string es una secuencia de BYTES, no de letras.

El texto se guarda con una codificación, normalmente UTF-8. En UTF-8, las letras del inglés ocupan 1 byte, pero la ñ, la á o un emoji ocupan 2, 3 o 4 bytes. Por eso strlen("piña") da 5 y no 4.

Las funciones mb_ (multibyte*) sí entienden de caracteres: mb_strlen, mb_substr, mb_strtoupper...

Pergamino del porque

¿Por qué PHP funciona así? Porque nació en 1995, cuando casi todo el texto era ASCII (1 byte por letra), y tratar bytes es rapidísimo. Las funciones mb_ llegaron después para los idiomas que no caben en ASCII... como el castellano. Además, strtoupper solo cambia letras ASCII: la ñ se queda igual.

Regla práctica: si tu texto puede tener tildes o eñes y te importa la longitud o cortar letras, usa mb_.

<?php
$fruta = "piña";
echo strlen($fruta), " bytes\n";
echo mb_strlen($fruta), " caracteres\n";
echo strtoupper($fruta), " / ", mb_strtoupper($fruta), "\n";
echo substr($fruta, 0, 3), "|\n";
echo mb_substr($fruta, 0, 3), "|\n";
Salida
5 bytes
4 caracteres
PIñA / PIÑA
pi�|
piñ|
Pregunta

¿Qué devuelve strlen("año")?

  1. 3
  2. 4
  3. 2
Ver respuesta

B. 4

La ñ ocupa 2 bytes en UTF-8: 1 + 2 + 1 = 4 bytes. mb_strlen("año") sí daría 3.

Cada letra tiene su número

Puedes leer un carácter (bueno, un byte) por su posición con corchetes. Las posiciones empiezan en 0, y los índices negativos cuentan desde el final.

$s = "Zend";
$s[0];    // "Z"
$s[-1];   // "d"
Cuidado

Si pides una posición que no existe, PHP da un Warning (Uninitialized string offset). Las posiciones válidas van de 0 a strlen($s) - 1.

<?php
$s = "Zend";
echo $s[0], $s[1], "\n";
echo $s[-1], "\n";
echo "Longitud: ", strlen($s), ", última posición: ", strlen($s) - 1, "\n";
Salida
Ze
d
Longitud: 4, última posición: 3

Dar formato: printf y sprintf

printf imprime un texto con huecos que se rellenan con valores; sprintf hace lo mismo pero devuelve el string en vez de imprimirlo.

  • %s: un string. %d: un entero. %.2f: un float con 2 decimales.
  • %5d: ancho mínimo 5 (rellena con espacios a la izquierda). %-8s: ancho 8 alineado a la izquierda. %05d: rellena con ceros.
  • %%: un símbolo % literal.

number_format($n, $decimales, ',', '.') formatea números al estilo español: 1.234,50.

<?php
printf("%s tiene %d sacos\n", "Vera", 12);
printf("Precio: %.2f monedas\n", 3.14159);
printf("[%5d] [%-8s] [%05d]\n", 42, "heno", 7);
$etiqueta = sprintf("Lote-%03d", 5);
echo $etiqueta, "\n";
echo number_format(1234.5, 2, ',', '.'), "\n";
printf("Batería al %d%%\n", 80);
Salida
Vera tiene 12 sacos
Precio: 3.14 monedas
[   42] [heno    ] [00007]
Lote-005
1.234,50
Batería al 80%

Textos largos: heredoc

Para textos de varias líneas existe la sintaxis heredoc: empieza con <<< y un identificador, y termina con el mismo identificador en su propia línea. Se comporta como las comillas dobles (interpola variables) y no necesitas escapar comillas.

Si usas <<<'EOT' (con comillas simples) se llama nowdoc y no interpola nada, como las comillas simples.

Truco

Desde PHP 7.3, el cierre puede estar indentado, y esa indentación se quita de todas las líneas. Muy cómodo.

<?php
$para = "Rasmus";
$sacos = 12;
$carta = <<<CARTA
    Querido $para:
    BIT-8 ha cosechado $sacos sacos.
    Un saludo, "Nadia Cadena".
    CARTA;
echo $carta, "\n";
Salida
Querido Rasmus:
BIT-8 ha cosechado 12 sacos.
Un saludo, "Nadia Cadena".

Ejercicios de Strings en PHP resueltos

Intenta resolverlos antes de mirar la solución. En el juego puedes ejecutarlos y comprobarlos automáticamente.

Encargo 1. Un mensaje por destinatario

Nadia Cadena manda la misma carta a muchos vecinos. Solo cambian el nombre y los sacos. Las variables $destinatario y $sacos ya existen (las rellena el Correo).

Imprime la carta usando las variables:

Querida Vera: BIT-8 ha cosechado 12 sacos.

Tu programa se probará con distintos vecinos, así que no escribas el nombre ni el número a mano.

Pistas
  • Dentro de comillas dobles puedes escribir $destinatario directamente.
  • echo "Querida $destinatario: BIT-8 ha cosechado $sacos sacos.";
Ver solución
<?php
echo "Querida $destinatario: BIT-8 ha cosechado $sacos sacos.";

Encargo 2. Etiquetas del almacén

El granero necesita etiquetas bien alineadas. Los nombres y cantidades están en $n1, $c1, $n2, $c2 y $n3, $c3.

Imprime una etiqueta por línea: el nombre relleno con puntos hasta 10 caracteres y la cantidad alineada a la derecha en 5 caracteres:

Heno......   40
Zanahoria.    7
Madera....  125
Pistas
  • str_pad($n1, 10, ".") rellena por la derecha con puntos.
  • Para la cantidad: str_pad($c1, 5, " ", STR_PAD_LEFT).
  • Con printf también se puede: printf("%'.-10s%5d\n", $n1, $c1); (el '. indica que se rellena con puntos).
Ver solución
<?php
printf("%'.-10s%5d\n", $n1, $c1);
printf("%'.-10s%5d\n", $n2, $c2);
printf("%'.-10s%5d\n", $n3, $c3);

Predicción 3. El paquete misterioso

Un paquete con una piña dentro. Nadia Cadena te reta a adivinar qué sale por pantalla.

Elige la salida exacta.

<?php
$fruta = "piña";
echo strlen($fruta), " ", mb_strlen($fruta), "\n";
echo 'Sin $fruta', "\n";
echo "Con {$fruta}s";
  1. 5 4 Sin $fruta Con piñas
  2. 4 4 Sin piña Con piñas
  3. 5 4 Sin piña Con piñas
  4. 4 5 Sin $fruta Con $frutas
Ver solución
5 4
Sin $fruta
Con piñas

¡Eso es! La ñ ocupa 2 bytes y las comillas simples no interpolan.

Caza de bugs 4. La transmisión corrupta

El telegrafista es nuevo y viene de otro lenguaje de programación. Su telegrama da un error rarísimo.

Arregla el programa para que imprima exactamente:

De: Cadena Para: Rasmus
Llega mañana

Código de partida:

<?php
$de = "Cadena";
$para = "Rasmus";
$mensaje = "De: " + $de + " Para: " + $para;
echo $mensaje, "\n";
echo 'Llega mañana\n';
Pistas
  • Lee el error: Unsupported operand types: string + string. En PHP, + solo suma números.
  • Para unir strings se usa el punto ..
  • Y en comillas simples \n no es un salto de línea.
Ver solución
<?php
$de = "Cadena";
$para = "Rasmus";
$mensaje = "De: " . $de . " Para: " . $para;
echo $mensaje, "\n";
echo "Llega mañana\n";

Misión del dron 5. BIT-8 aprende a hablar

BIT-8 tiene un altavoz: decir($texto) le hace mostrar un bocadillo. Nadia Cadena quiere que informe de su trabajo.

Cosecha la hierba de debajo y la de la casilla del NORTE. Después haz que BIT-8 diga exactamente Tengo 2 de heno, construyendo el texto con el valor de inventario(HENO) (sin escribir el 2 a mano).

Esta misión mueve al dron BIT-8 por un campo de cultivo: juégala en PHPlay para ver tu código animado.

Pistas
  • Guarda el inventario en una variable: $heno = inventario(HENO);
  • decir("Tengo $heno de heno");
Ver solución
<?php
cosechar();
mover(NORTE);
cosechar();
$heno = inventario(HENO);
decir("Tengo $heno de heno");

Chuleta: lo esencial

  • Comillas simples: texto literal. Dobles: interpretan \n, \t, \$ y variables.
  • Interpolación: "Hola $nombre", "{$fruta}s", "{$arr['k']}".
  • Concatenar: $a . $b; añadir: $s .= "más";. En PHP 8, + y - van antes que el punto.
  • Funciones: strlen, strtoupper, ucfirst, str_repeat, str_replace, strpos, str_contains, trim, substr, str_pad. Devuelven un string nuevo.
  • Un string son bytes: strlen("ñ") = 2. Usa mb_strlen, mb_substr, mb_strtoupper con tildes/eñes.
  • $s[0] primer carácter, $s[-1] el último.
  • printf("%s %d %.2f %5d %-8s %05d %%", ...); sprintf devuelve el texto; number_format($n, 2, ',', '.').
  • Heredoc <<<ID ... ID; (interpola) y nowdoc <<<'ID' (no interpola).