Mediante el uso de la función wordwrap . Divide los textos en varias líneas, de modo que el ancho máximo es el que especificó, rompiendo los límites de las palabras. Después de dividir, simplemente toma la primera línea:
substr($string, 0, strpos(wordwrap($string, $your_desired_width), "\n"));
Una cosa que este oneliner no maneja es el caso cuando el texto en sí es más corto que el ancho deseado. Para manejar este caso límite, uno debe hacer algo como:
if (strlen($string) > $your_desired_width)
{
$string = wordwrap($string, $your_desired_width);
$string = substr($string, 0, strpos($string, "\n"));
}
La solución anterior tiene el problema de cortar prematuramente el texto si contiene una nueva línea antes del punto de corte real. Aquí una versión que resuelve este problema:
function tokenTruncate($string, $your_desired_width) {
$parts = preg_split('/([\s\n\r]+)/', $string, null, PREG_SPLIT_DELIM_CAPTURE);
$parts_count = count($parts);
$length = 0;
$last_part = 0;
for (; $last_part < $parts_count; ++$last_part) {
$length += strlen($parts[$last_part]);
if ($length > $your_desired_width) { break; }
}
return implode(array_slice($parts, 0, $last_part));
}
Además, aquí está la clase de prueba PHPUnit utilizada para probar la implementación:
class TokenTruncateTest extends PHPUnit_Framework_TestCase {
public function testBasic() {
$this->assertEquals("1 3 5 7 9 ",
tokenTruncate("1 3 5 7 9 11 14", 10));
}
public function testEmptyString() {
$this->assertEquals("",
tokenTruncate("", 10));
}
public function testShortString() {
$this->assertEquals("1 3",
tokenTruncate("1 3", 10));
}
public function testStringTooLong() {
$this->assertEquals("",
tokenTruncate("toooooooooooolooooong", 10));
}
public function testContainingNewline() {
$this->assertEquals("1 3\n5 7 9 ",
tokenTruncate("1 3\n5 7 9 11 14", 10));
}
}
EDITAR:
No se manejan caracteres especiales UTF8 como 'à'. Agregue 'u' al final del REGEX para manejarlo:
$parts = preg_split('/([\s\n\r]+)/u', $string, null, PREG_SPLIT_DELIM_CAPTURE);