Content-Negotiation with the help of Renderer and Formatters
parent
c0159f4b79
commit
7e0a38e989
@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BSR\Lib\Formatter;
|
||||||
|
|
||||||
|
abstract class Formatter {
|
||||||
|
private static $formats = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $formats New available formats, array(mimetype => class)
|
||||||
|
*/
|
||||||
|
protected static function registerFormats(array $formats) {
|
||||||
|
foreach($formats as $f) {
|
||||||
|
self::$formats[$f] = get_called_class();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Formatter The formatter to use for this request
|
||||||
|
*/
|
||||||
|
public static function getFormatter() {
|
||||||
|
self::loadFormatters();
|
||||||
|
$format = self::getFormatFromHeader();
|
||||||
|
|
||||||
|
return new $format();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all formatters in the current directory
|
||||||
|
*/
|
||||||
|
private static function loadFormatters() {
|
||||||
|
preg_match('/(.+)\\\([a-zA-Z0-9]+)/', get_called_class(), $parts);
|
||||||
|
$us = $parts[2];
|
||||||
|
$namespace = $parts[1];
|
||||||
|
|
||||||
|
$base = __DIR__.'/';
|
||||||
|
$ext = '.php';
|
||||||
|
$files = glob(sprintf('%s%s%s', $base, '*', $ext));
|
||||||
|
foreach($files as $f) {
|
||||||
|
$c = str_replace(array($base, $ext), '', $f);
|
||||||
|
if($c !== $us) {
|
||||||
|
$c = $namespace.'\\'.$c;
|
||||||
|
call_user_func(array($c, 'init'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string The class name to instantiate in accord to the Accept header
|
||||||
|
*/
|
||||||
|
private static function getFormatFromHeader() {
|
||||||
|
if(isset($_SERVER['HTTP_ACCEPT'])) {
|
||||||
|
$formats = array_map(function($f) {
|
||||||
|
$parts = explode(';', $f);
|
||||||
|
$parts[1] = (isset($parts[1]) ? (float) preg_replace('/[^0-9\.]/', '', $parts[1]) : 1.0) * 100;
|
||||||
|
return $parts;
|
||||||
|
}, explode(',', $_SERVER['HTTP_ACCEPT']));
|
||||||
|
|
||||||
|
usort($formats, function($a, $b) { return $b[1] - $a[1]; });
|
||||||
|
|
||||||
|
foreach($formats as $f) {
|
||||||
|
if(isset(self::$formats[$f[0]])) {
|
||||||
|
return self::$formats[$f[0]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'BSR\Lib\Formatter\Json';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output the content for the given data
|
||||||
|
* @param $array data
|
||||||
|
*/
|
||||||
|
abstract public function render($data);
|
||||||
|
}
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BSR\Lib\Formatter;
|
||||||
|
|
||||||
|
class Html extends Formatter {
|
||||||
|
protected static function init() {
|
||||||
|
self::registerFormats(array(
|
||||||
|
'application/xhtml+xml',
|
||||||
|
'text/html',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function result($data) {
|
||||||
|
// the format is array('result' => array('funcname' => DATA))
|
||||||
|
// so take the first element of the 'result' array
|
||||||
|
$data = reset($data['result']);
|
||||||
|
|
||||||
|
$first = reset($data);
|
||||||
|
$single = ! is_array($first);
|
||||||
|
|
||||||
|
$content = '<table><thead>';
|
||||||
|
if($single) {
|
||||||
|
$content .= "<tr><th>Field</th><th>Value</th></tr>";
|
||||||
|
} else {
|
||||||
|
$content .= '<tr>';
|
||||||
|
foreach(array_keys($first) as $k) {
|
||||||
|
$content .= "<th>$k</th>";
|
||||||
|
}
|
||||||
|
$content .= '</tr>';
|
||||||
|
}
|
||||||
|
$content .= '</thead><tbody>';
|
||||||
|
if($single) {
|
||||||
|
foreach($data as $k => $v) {
|
||||||
|
$content .= "<tr><th>$k</th><td>".print_r($v, true)."</td></tr>";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach($data as $row) {
|
||||||
|
$content .= '<tr>';
|
||||||
|
foreach($row as $col) {
|
||||||
|
$content .= '<td>'.print_r($col, true).'</td>';
|
||||||
|
}
|
||||||
|
$content .= '</tr>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$content .= '</tbody></table>';
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function error($data) {
|
||||||
|
$code = $data['error']['code'];
|
||||||
|
$name = $data['error']['name'];
|
||||||
|
$msg = $data['error']['reason'];
|
||||||
|
|
||||||
|
return '<h1>An error occured</h1>'.
|
||||||
|
"<h2>[$code] $name</h2>".
|
||||||
|
"<p>$msg</p>";
|
||||||
|
}
|
||||||
|
|
||||||
|
private function failure($data) {
|
||||||
|
$code = $data['failure']['code'];
|
||||||
|
$name = $data['failure']['reason'];
|
||||||
|
|
||||||
|
return '<h1>A failure occured</h1>'.
|
||||||
|
"<h2>[$code] $name</h2>";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render($data)
|
||||||
|
{
|
||||||
|
$type = key($data);
|
||||||
|
|
||||||
|
if (method_exists($this, $type)) {
|
||||||
|
$content = call_user_func_array(array($this, $type), array($data));
|
||||||
|
} else {
|
||||||
|
$content = '<h1>Unable to render this</h1>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo $content;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BSR\Lib\Formatter;
|
||||||
|
|
||||||
|
class Json extends Formatter {
|
||||||
|
protected static function init() {
|
||||||
|
self::registerFormats(array(
|
||||||
|
'application/json',
|
||||||
|
'application/x-json',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render($data) {
|
||||||
|
echo json_encode($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BSR\Lib\Formatter;
|
||||||
|
|
||||||
|
class Text extends Formatter {
|
||||||
|
protected static function init() {
|
||||||
|
self::registerFormats(array('text/plain'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render($data) {
|
||||||
|
print_r($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BSR\Lib\Formatter;
|
||||||
|
|
||||||
|
class Xml extends Formatter {
|
||||||
|
protected static function init() {
|
||||||
|
self::registerFormats(array(
|
||||||
|
'text/xml',
|
||||||
|
'application/xml',
|
||||||
|
'application/x-xml',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render($data) {
|
||||||
|
throw new \RuntimeException('Not implemented yet.');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BSR\Lib;
|
||||||
|
|
||||||
|
use BSR\Lib\Formatter\Formatter;
|
||||||
|
|
||||||
|
class Renderer {
|
||||||
|
private static $statusMessages = array(
|
||||||
|
200 => 'Ok',
|
||||||
|
400 => 'Bad request',
|
||||||
|
404 => 'Not Found',
|
||||||
|
403 => 'Not Authorized',
|
||||||
|
500 => 'Server Error',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
ob_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render($status, $data) {
|
||||||
|
header(sprintf('HTTP/1.0 %s %s', $status, self::$statusMessages[$status]));
|
||||||
|
|
||||||
|
ob_clean();
|
||||||
|
flush();
|
||||||
|
|
||||||
|
$formatter = Formatter::getFormatter();
|
||||||
|
$formatter->render($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue