WordPress e l’annoso problema degli spazi bianchi

Non è la prima volta che accade e non è la prima volta che ricorro a questo tipo di soluzione, quindi è venuto il momento di condividere con chi mai dovesse incappare nello stesso problema in futuro.

Facciamo spesso molta integrazione tra applicazioni di vario tipo e WordPress. In molti casi queste integrazioni prevedono la generazione di file (XML, PDF, etc.) attingendo direttamente alle classi di WordPress ma da applicazioni esterne, principalmente gestionali amministrativi e CRM.

Lo scenario è abbastanza semplice: richiamiamo le funzionalità di base di WordPress dall’interno di un’altra applicazione – in modalità themeless – e lavoriamo sui suoi dati senza agire direttamente su database e filesystem. Questo approccio ha numerosi vantaggi in termini di sicurezza e performance. Inoltre permette la massima coerenza possibile rendendo il codice dell’applicazione esterna indipendente da ogni futuro aggiornamento di WordPress.

Ecco un semplice esempio di come effettuare l’inclusione di WordPress in un’applicazione esterna:

define('WP_USE_THEMES', false);
define('WP_USE_PLUGINS', false);
require($_SERVER['DOCUMENT_ROOT'].'/wp-load.php');

Succede però – abbastanza raramente, per fortuna – che il caricamento delle classi di WordPress generi immediatamente spazi bianchi o ritorni a capo, prima ancora che venga chiamato in causa il codice che genera l’output desiderato dall’applicazione esterna. Lo si può vedere ad esempio qui:

L’origine del problema risiede quasi sempre in uno dei plugin installati oppure in funzionalità interne del tema attivo, non dal core di WordPress. Si tratta per intenderci di veri e propri bug – per quanto apparentemente insignificanti – e sono sintomo di codice scritto in maniera disordinata.

La presenza di spazi bianchi o di intere righe prima dell’output vero e proprio non dà grossi problemi in ambito HTML perché i browser si limitano ad ignorarli, ma invalida completamente i file XML rendendo di fatto impossibile la loro generazione a causa del seguente errore:

Questo ha conseguenze ancora più infauste nel caso in cui la generazione dell’XML sia un passaggio intermedio per l’elaborazione di altri formati, ad esempio nella produzione di un PDF.

Dico che le conseguenze sono ancora più infauste perché individuare la causa dell’errore e risalire al motivo per cui otteniamo sempre un PDF bianco o addirittura il nulla può costare molte ore di analisi.

Una volta capito che si tratta di questo problema si possono seguire più strade, una molto empirica e anche un po’ ottimistica la propone W3C ed è ben illustrara qui:

fonte: https://bit.ly/2I7s3LF

Un’altra soluzione tanto banale quanto brillante è invece lo script WordPress leading whitespace fix realizzato ormai anni or sono dallo sviluppatore Michal “Wejn” Jirků sotto licenza MIT – riposto qui il codice originale per comodità mia:

/*
Author: Michal "Wejn" Jirků {box at wejn dot org}
License: MIT
Version: 2.1
Changelog:
- Added better mime-type detection
- Now works even when C-T header not set
- Changed intro text to better target keywords
- [2.1] Added tip from Eric Auer
*/

function ___wejns_wp_whitespace_fix($input) {
	/* valid content-type? */
	$allowed = false;

	/* found content-type header? */
	$found = false;

	/* we mangle the output if (and only if) output type is text/* */
	foreach (headers_list() as $header) {
		if (preg_match("/^content-type:\\s+(text\\/|application\\/((xhtml|atom|rss)\\+xml|xml))/i", $header)) {
			$allowed = true;
		}

		if (preg_match("/^content-type:\\s+/i", $header)) {
			$found = true;
		}
	}

	/* do the actual work */
	if ($allowed || !$found) {
		return preg_replace("/\\A\\s*/m", "", $input);
	} else {
		return $input;
	}
}

/* start output buffering using custom callback */
ob_start("___wejns_wp_whitespace_fix");

In pratica, questo script prende in carico l’output di WordPress e lo trattiene fino alla sua completa elaborazione per poi restituirlo ripulito dai famigerati spazi bianchi iniziali.

Il file può essere posizionato nella document root di WordPress e incluso come nell’esempio seguente:

define('WP_USE_THEMES', false);
define('WP_USE_PLUGINS', false);
include($_SERVER['DOCUMENT_ROOT'].'/wejnswpwhitespacefix.php');
require($_SERVER['DOCUMENT_ROOT'].'/wp-load.php');

Attenzione alla puntualizzazione fatta dallo stesso Michael che riporta un’email dello sviluppatore Eric Auer, molto importante perché suggerisce un uso alternativo (e temporaneo) dello script:

Dear Wejn, thanks for writing wejnswpwhitespacefix.php! Apart from using it as FIX in the way described in your code, the fix can also be used as TOOL to DEBUG whitespace problems.

Here is how:

  1. download your script
  2. do not put the include(“wejnswpwhitespacefix.php”); at the recommended location, but put it at a LATER place
  3. whenever the script is included BEFORE whatever sends the extra whitespace, it fixes the symptom. Otherwise, it cannot fix the symptom.
  4. move around the include and watch when there is, or is not, extra whitespace (eg. view HTML source or RSS XML)
  5. you found the offending PHP file 🙂

In our case, I went via wp-blog-header.php, wp-load.php, wp-config.php to wp-settings.php and found that things broke at the moment when TEMPLATEPATH/functions.php got loaded. It turned out that our “devio” theme functions.php file had whitespace after a final ?> tag. Because outside the php tags, everything is raw content, that whitespace got added to every webpage and feed. Yuck! By either removing the whitespace or the whole ?> tag, the problem went away. Great! Maybe this trick is worth mentioning in the “user manual” of your whitespace fix, for those who do not only want to work around the problem but find the source of it as well.

Non c’è che dire, ogni volta che torno su argomenti di questo tipo mi sorprende la capacità della comunità open source di produrre soluzioni brillanti a costo zero. Quando si dice che la conoscenza è patrimonio di tutti.

Condivido

Leave a comment