58 lines
1.7 KiB
PHP
58 lines
1.7 KiB
PHP
<?php
|
|
|
|
namespace App\BusProNet;
|
|
|
|
use Symfony\Component\DomCrawler\Crawler;
|
|
|
|
/**
|
|
* Builds a Crawler over BusPro's XML payloads.
|
|
*
|
|
* The Crawler constructor parses its content as HTML5, which flattens the
|
|
* nesting BusPro's exports and API responses rely on, so XML has to be added
|
|
* through addXmlContent() instead.
|
|
*/
|
|
final class XmlCrawlerFactory
|
|
{
|
|
public static function create(string $xml): Crawler
|
|
{
|
|
$crawler = new Crawler();
|
|
$crawler->addXmlContent($xml);
|
|
|
|
return $crawler;
|
|
}
|
|
|
|
/**
|
|
* Explains why create() returned an empty Crawler. addXmlContent() discards the
|
|
* libxml errors, so an empty, a malformed and a truncated payload are
|
|
* indistinguishable afterwards; this re-parses to recover the reason. Only worth
|
|
* calling on the failure path.
|
|
*/
|
|
public static function diagnose(string $xml): string
|
|
{
|
|
if ('' === trim($xml)) {
|
|
return sprintf('empty response (%d bytes)', strlen($xml));
|
|
}
|
|
|
|
$previousUseErrors = libxml_use_internal_errors(true);
|
|
libxml_clear_errors();
|
|
|
|
try {
|
|
simplexml_load_string($xml);
|
|
|
|
$messages = [];
|
|
foreach (libxml_get_errors() as $error) {
|
|
$messages[] = sprintf('%s (line %d, column %d)', trim($error->message), $error->line, $error->column);
|
|
}
|
|
|
|
if ([] === $messages) {
|
|
return sprintf('unknown XML error (%d bytes)', strlen($xml));
|
|
}
|
|
|
|
return sprintf('%s (%d bytes)', implode('; ', array_unique($messages)), strlen($xml));
|
|
} finally {
|
|
libxml_clear_errors();
|
|
libxml_use_internal_errors($previousUseErrors);
|
|
}
|
|
}
|
|
}
|