using composer package format, decoupling bsr libraries and implementation
This commit is contained in:
73
src/Configuration.php
Normal file
73
src/Configuration.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
namespace BSR\Lib;
|
||||
|
||||
class Configuration {
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* ! WARNING !
|
||||
*
|
||||
* Those are default values, if you need to change them for a particular host,
|
||||
* please just create a file named 'configuration.local.php' in the same directory
|
||||
* and redefine the values there !
|
||||
*
|
||||
* This file must contain an array named $configuration containing the keys you
|
||||
* want to redefine. Then, those values will be used to replace those in the
|
||||
* array defined just below.
|
||||
*
|
||||
* @var array configuration values
|
||||
*/
|
||||
private $values = array();
|
||||
|
||||
private $custom_config_filename = 'configuration.local.php';
|
||||
|
||||
private function __construct() {
|
||||
// by default, set the session save path to the default value;
|
||||
$this->values['session']['save_path'] = session_save_path();
|
||||
|
||||
if(!file_exists($this->custom_config_filename)) {
|
||||
throw new \RuntimeException('No configuration.local.php file was found. Create it with the proper config.');
|
||||
}
|
||||
|
||||
$configuration = include_once realpath(dirname(__FILE__) . '/../config/') . $this->custom_config_filename;
|
||||
|
||||
if(!is_array($configuration)) {
|
||||
throw new \RuntimeException("You custom configuration in '{$this->custom_config_filename}' must be in a variable named '\$configuration' and be an array.");
|
||||
}
|
||||
|
||||
$this->values = array_replace_recursive($this->values, $configuration);
|
||||
}
|
||||
|
||||
private function dotNotationAccess($data, $key, $default=null)
|
||||
{
|
||||
$keys = explode('.', $key);
|
||||
foreach ($keys as $k) {
|
||||
if (!is_array($data)) {
|
||||
throw new \Exception("Try to access non-array as array, key '$key''");
|
||||
}
|
||||
if (!isset($data[$k])) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$data = $data[$k];
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function value($name, $default) {
|
||||
return $this->dotNotationAccess($this->values, $name, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param mixed $default the default value for your configuration option
|
||||
* @return mixed return the configuration value if the key is find, the default otherwise
|
||||
*/
|
||||
public static function get($name, $default = null) {
|
||||
if(is_null(self::$instance)) {
|
||||
self::$instance = new Configuration();
|
||||
}
|
||||
|
||||
return self::$instance->value($name, $default);
|
||||
}
|
||||
}
|
||||
16
src/Exception/AuthenticationException.php
Normal file
16
src/Exception/AuthenticationException.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace BSR\Lib\Exception;
|
||||
|
||||
/**
|
||||
* This exception should be raised when an error
|
||||
* related to the authentication mechanism arise.
|
||||
*
|
||||
* @package BSR\Lib\Exception
|
||||
*/
|
||||
class AuthenticationException extends WebException {
|
||||
const USER_NOT_FOUND = 200;
|
||||
const BAD_LOGIN = 201;
|
||||
const AUTHENTICATION_FAILED = 202;
|
||||
const LOGIN_EMPTY = 203;
|
||||
}
|
||||
8
src/Exception/InvalidAttributeException.php
Normal file
8
src/Exception/InvalidAttributeException.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace BSR\Lib\Exception;
|
||||
|
||||
/**
|
||||
* Exception raised when an invalid attribute name is accessed
|
||||
*/
|
||||
class InvalidAttributeException extends \Exception { }
|
||||
19
src/Exception/SqlException.php
Normal file
19
src/Exception/SqlException.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace BSR\Lib\Exception;
|
||||
|
||||
class SqlException extends \Exception
|
||||
{
|
||||
private $query;
|
||||
|
||||
public function __construct($message = "Sql Error", $query = "")
|
||||
{
|
||||
$this->query = $query;
|
||||
parent::__construct($message, 0);
|
||||
}
|
||||
|
||||
public function getSqlError()
|
||||
{
|
||||
return $this->getMessage().' while executing: '.$this->query;
|
||||
}
|
||||
}
|
||||
19
src/Exception/UsageException.php
Normal file
19
src/Exception/UsageException.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace BSR\Lib\Exception;
|
||||
|
||||
/**
|
||||
* This exception should be raised by the WebService engine when
|
||||
* there is an error preventing the correct calling of a method.
|
||||
*
|
||||
* @package BSR\Lib\Exception
|
||||
*/
|
||||
class UsageException extends WebException {
|
||||
const NO_ARGS = 100;
|
||||
|
||||
const MISSING_METHOD = 101;
|
||||
const BAD_METHOD = 102;
|
||||
|
||||
const TOO_FEW_ARGS = 103;
|
||||
const TOO_MANY_ARGS = 104;
|
||||
}
|
||||
24
src/Exception/WebException.php
Normal file
24
src/Exception/WebException.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace BSR\Lib\Exception;
|
||||
|
||||
class WebException extends \Exception
|
||||
{
|
||||
private $exceptionName;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $reason
|
||||
* @param int $code
|
||||
*/
|
||||
function __construct($name, $reason, $code)
|
||||
{
|
||||
$this->exceptionName = $name;
|
||||
parent::__construct($reason, $code);
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->exceptionName;
|
||||
}
|
||||
}
|
||||
78
src/Formatter/Formatter.php
Normal file
78
src/Formatter/Formatter.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?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() {
|
||||
//TODO this is ugly
|
||||
return 'BSR\Lib\Formatter\Json';
|
||||
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);
|
||||
}
|
||||
157
src/Formatter/Html.php
Normal file
157
src/Formatter/Html.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace BSR\Lib\Formatter;
|
||||
|
||||
use BSR\Lib\Logger;
|
||||
|
||||
class Html extends Formatter {
|
||||
protected static function init() {
|
||||
self::registerFormats(array(
|
||||
'application/xhtml+xml',
|
||||
'text/html',
|
||||
));
|
||||
}
|
||||
|
||||
protected function truncate($v, $ellipsis = ' [...]') {
|
||||
$limit = 50;
|
||||
if(strlen($v) > $limit) {
|
||||
$v = substr($v, 0, $limit).$ellipsis;
|
||||
}
|
||||
return $v;
|
||||
}
|
||||
|
||||
protected function formatValue($v) {
|
||||
if(is_numeric($v)) {
|
||||
return $v;
|
||||
}
|
||||
if(is_bool($v)) {
|
||||
return '<span class="glyphicon glyphicon-'.($v ? 'ok' : 'remove').'"></span>';
|
||||
}
|
||||
if(is_string($v) && strpos($v, 'http') !== false) {
|
||||
$url = $v;
|
||||
$v = $this->truncate($v, '...');
|
||||
return "<a href='$url' target='_blank'>$v</a>";
|
||||
}
|
||||
|
||||
return $this->truncate(print_r($v, true));
|
||||
}
|
||||
|
||||
protected function result($data) {
|
||||
// the format is array('result' => array('function name' => DATA))
|
||||
// so take the first element of the 'result' array
|
||||
$func = key($data['result']);
|
||||
$data = reset($data['result']);
|
||||
$data = is_array($data) ? $data : array();
|
||||
$title = $func;
|
||||
|
||||
$content = '';
|
||||
$after = '';
|
||||
|
||||
if($func == 'NewSearch') {
|
||||
$content .= '<p>Count : '.$data['count'].'</p>';
|
||||
$after .= '<p>Extra : <pre>'.print_r($data['facets'], true).'</pre></p>';
|
||||
|
||||
unset($data['count']);
|
||||
unset($data['facets']);
|
||||
}
|
||||
|
||||
$first = reset($data);
|
||||
$single = ! is_array($first);
|
||||
$columns = array();
|
||||
|
||||
$content .= '<table class="table table-striped table-hover table-condensed table-responsive"><thead>';
|
||||
if($single) {
|
||||
$content .= "<tr><th>Field</th><th>Value</th></tr>";
|
||||
} else {
|
||||
$columns = array_keys($first);
|
||||
|
||||
$title .= ' ('.count($data).' results)';
|
||||
|
||||
$content .= '<tr>';
|
||||
foreach($columns as $k) {
|
||||
$content .= "<th>$k</th>";
|
||||
}
|
||||
$content .= '</tr>';
|
||||
}
|
||||
$content .= '</thead><tbody>';
|
||||
if($single) {
|
||||
foreach($data as $k => $v) {
|
||||
$content .= "<tr><th>$k</th><td>".$this->formatValue($v)."</td></tr>";
|
||||
}
|
||||
} else {
|
||||
foreach($data as $row) {
|
||||
$content .= '<tr>';
|
||||
foreach($columns as $c) {
|
||||
$content .= '<td>'.$this->formatValue(isset($row[$c]) ? $row[$c] : '').'</td>';
|
||||
}
|
||||
$content .= '</tr>';
|
||||
}
|
||||
}
|
||||
$content .= '</tbody></table>'.$after;
|
||||
|
||||
return array(
|
||||
'title' => $title,
|
||||
'content' => $content,
|
||||
'status' => 'success',
|
||||
);
|
||||
}
|
||||
|
||||
protected function error($data) {
|
||||
$code = $data['error']['code'];
|
||||
$name = $data['error']['name'];
|
||||
$msg = $data['error']['reason'];
|
||||
|
||||
return array(
|
||||
'title' => 'Error',
|
||||
'content' => "<h2>[$code] $name : $msg</h2>",
|
||||
'status' => 'warning',
|
||||
);
|
||||
}
|
||||
|
||||
protected function failure($data) {
|
||||
$code = $data['failure']['code'];
|
||||
$name = $data['failure']['reason'];
|
||||
|
||||
return array(
|
||||
'title' => 'Failure',
|
||||
'content' => "<h2>[$code] $name</h2>",
|
||||
'status' => 'danger',
|
||||
);
|
||||
}
|
||||
|
||||
public function render($data)
|
||||
{
|
||||
$type = key($data);
|
||||
|
||||
if (method_exists($this, $type)) {
|
||||
$context = call_user_func_array(array($this, $type), array($data));
|
||||
} else {
|
||||
$context = array(
|
||||
'title' => 'Formatter error',
|
||||
'content' => '<h1>Unable to render this</h1>',
|
||||
'status' => 'info',
|
||||
);
|
||||
}
|
||||
$info = Logger::data();
|
||||
$context['time'] = $info['time'];
|
||||
|
||||
$panel = static::template($context, 'panel');
|
||||
|
||||
if(isset($data['extra'])) {
|
||||
$panel .= $data['extra'];
|
||||
}
|
||||
|
||||
echo static::template(array(
|
||||
'version' => $info['version'],
|
||||
'title' => $context['title'],
|
||||
'content' => $panel,
|
||||
));
|
||||
}
|
||||
|
||||
public static function template(array $context = array(), $template = 'layout') {
|
||||
$html = file_get_contents(sprintf('templates/%s.html', $template));
|
||||
|
||||
$patterns = array_map(function($p) { return "{{ $p }}"; }, array_keys($context));
|
||||
return str_replace($patterns, array_values($context), $html);
|
||||
}
|
||||
}
|
||||
17
src/Formatter/Json.php
Normal file
17
src/Formatter/Json.php
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
13
src/Formatter/Text.php
Normal file
13
src/Formatter/Text.php
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
17
src/Formatter/Xml.php
Normal file
17
src/Formatter/Xml.php
Normal file
@@ -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.');
|
||||
}
|
||||
}
|
||||
83
src/Help.php
Normal file
83
src/Help.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace BSR\Lib;
|
||||
|
||||
use BSR\Lib\Exception\WebException;
|
||||
use BSR\Lib\Formatter\Html;
|
||||
|
||||
class Help {
|
||||
private static function func($ws, $func) {
|
||||
try {
|
||||
$rm = new \ReflectionMethod($ws, $func);
|
||||
} catch(\ReflectionException $e) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$doc = $rm->getDocComment();
|
||||
$params = $rm->getParameters();
|
||||
|
||||
preg_match_all('/@param\s+(?P<type>[^\s]*?)\s*\$(?P<name>[^\s]+?)\s+(?P<doc>[\w\s]*)/', $doc, $parametersDoc, PREG_SET_ORDER);
|
||||
preg_match('/@return\s+(?P<type>[^\s]*?)\s+(?P<doc>[\w\s]*)/', $doc, $returnDoc);
|
||||
|
||||
$doc = array_filter(array_map(function($l) {
|
||||
$l = trim($l, " \t\n\r\0\x0B");
|
||||
if(strpos($l, '/**') === 0 || strpos($l, '*/') === 0) {
|
||||
$l = '';
|
||||
}
|
||||
$l = trim($l, "* ");
|
||||
if(strpos($l, '@') === 0) {
|
||||
$l = '';
|
||||
}
|
||||
return $l;
|
||||
}, explode("\n", $doc)));
|
||||
$doc = nl2br(implode("\n", $doc));
|
||||
|
||||
$paramsHtml = '';
|
||||
foreach($params as $p) {
|
||||
foreach($parametersDoc as $d) {
|
||||
if(isset($d['name']) && $p->name == $d['name']) {
|
||||
$paramsHtml .= Html::template(array(
|
||||
'name' => $p->name,
|
||||
'optional' => $p->isDefaultValueAvailable() ? '(optional)' : '',
|
||||
'type' => isset($d['type']) ? $d['type'] : '',
|
||||
'doc' => isset($d['doc']) ? $d['doc'] : '',
|
||||
), 'param_help');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Html::template(array(
|
||||
'func' => $func,
|
||||
'help' => $doc,
|
||||
'parameters' => $paramsHtml,
|
||||
'return' => Html::template(array(
|
||||
'type' => isset($returnDoc['type']) ? $returnDoc['type'] : '',
|
||||
'doc' => isset($returnDoc['doc']) ? $returnDoc['doc'] : '',
|
||||
), 'return_help'),
|
||||
), 'func_help');
|
||||
}
|
||||
|
||||
|
||||
public static function content(WebService $ws) {
|
||||
$rc = new \ReflectionClass($ws);
|
||||
|
||||
$methods = array_filter(array_map(function(\ReflectionMethod $m) {
|
||||
if($m->getName() == 'Run') {
|
||||
// this is a method from WebService directly and is of not interests for the help
|
||||
return '';
|
||||
}
|
||||
return $m->getName();
|
||||
}, $rc->getMethods(\ReflectionMethod::IS_PUBLIC)));
|
||||
|
||||
$html = '';
|
||||
foreach($methods as $m) {
|
||||
$html .= static::func($ws, $m);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public static function exception(WebException $e, WebService $ws, $func) {
|
||||
return static::func($ws, $func);
|
||||
}
|
||||
}
|
||||
156
src/Logger.php
Normal file
156
src/Logger.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace BSR\Lib;
|
||||
|
||||
class Logger {
|
||||
const QUIET = 0;
|
||||
const NORMAL = 1;
|
||||
const VERBOSE = 2;
|
||||
|
||||
private static $start;
|
||||
private static $data = array();
|
||||
private static $log = '';
|
||||
|
||||
private static function ip() {
|
||||
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
return array_shift(
|
||||
array_map('trim', explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']))
|
||||
);
|
||||
} else if (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
return $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
|
||||
return '(n-a)';
|
||||
}
|
||||
|
||||
public static function start($data = array()) {
|
||||
self::$start = microtime(true);
|
||||
|
||||
self::$data = $data + array(
|
||||
'ip' => self::ip(),
|
||||
'date' => date('d.m.Y H:i:s'),
|
||||
'func' => '(none)',
|
||||
'version' => '(none)',
|
||||
'error' => ''
|
||||
);
|
||||
}
|
||||
|
||||
public static function info($info, $key = null) {
|
||||
if(is_null($key)) {
|
||||
self::$data = array_merge(self::$data, $info);
|
||||
} else {
|
||||
self::$data[$key] = $info;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message that will be displayed in the logs if the configuration
|
||||
* says so.
|
||||
*
|
||||
* @param string $message
|
||||
* @param int $verbosity
|
||||
*/
|
||||
public static function log($message, $verbosity = Logger::VERBOSE) {
|
||||
if(Configuration::get('log.verbosity') < $verbosity) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$log .= $message."\n";
|
||||
}
|
||||
|
||||
public static function stop($data = null) {
|
||||
if(! is_null($data)) {
|
||||
self::info($data);
|
||||
}
|
||||
|
||||
$time = (microtime(true) - self::$start) * 1000;
|
||||
self::$data['time'] = round($time, 2).'ms';
|
||||
|
||||
if(Configuration::get('log.verbosity') > Logger::QUIET) {
|
||||
$format = Configuration::get('log.format');
|
||||
|
||||
$patterns = array_map(function($p) { return "%$p%"; }, array_keys(self::$data));
|
||||
$msg = str_replace($patterns, array_values(self::$data), $format)."\n";
|
||||
|
||||
if(strlen(self::$log) > 0) {
|
||||
$msg .= self::$log;
|
||||
}
|
||||
|
||||
$file = Configuration::get('log.file');
|
||||
if(! file_exists($file)) {
|
||||
mkdir(dirname($file), 0777, true);
|
||||
touch($file);
|
||||
} else {
|
||||
$mtime = filemtime($file);
|
||||
// start of the current day
|
||||
$start = strtotime("midnight");
|
||||
|
||||
// log rotate if the last entry in the log is from yesterday
|
||||
if($mtime < $start) {
|
||||
$files = glob($file.'.?');
|
||||
natsort($files);
|
||||
$files = array_reverse($files);
|
||||
$files[] = $file;
|
||||
|
||||
// we count before adding the next file
|
||||
$len = count($files);
|
||||
|
||||
$next = intval(substr($files[0], -1)) + 1;
|
||||
$next = $next == 1 ? "$file.1" : substr($files[0], 0, -1).$next;
|
||||
|
||||
array_unshift($files, $next);
|
||||
for($i = 0; $i < $len; ++$i) {
|
||||
// move all the log files to the next name
|
||||
rename($files[$i + 1], $files[$i]);
|
||||
}
|
||||
|
||||
// delete all files with a number bigger than 9
|
||||
$files = glob($file.'.??');
|
||||
array_map('unlink', $files);
|
||||
|
||||
// recreate the new log file
|
||||
touch($file);
|
||||
}
|
||||
}
|
||||
file_put_contents($file, $msg, FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
}
|
||||
|
||||
public static function data() {
|
||||
return self::$data;
|
||||
}
|
||||
|
||||
public static function getLastLogs($offset = null) {
|
||||
$file = Configuration::get('log.file');
|
||||
if(! file_exists($file)) {
|
||||
return 'No log yet !';
|
||||
}
|
||||
|
||||
$f = fopen($file, 'r');
|
||||
|
||||
$len = 8192;
|
||||
|
||||
fseek($f, 0, SEEK_END);
|
||||
$size = ftell($f);
|
||||
if(is_null($offset) || $offset > $size) {
|
||||
$offset = $size - $len;
|
||||
}
|
||||
$offset = max(0, $offset);
|
||||
|
||||
fseek($f, $offset);
|
||||
|
||||
// remove the first line that may be incomplete
|
||||
$buffer = fread($f, $len);
|
||||
$buffer = explode("\n", $buffer);
|
||||
array_shift($buffer);
|
||||
$buffer = implode("\n", $buffer);
|
||||
// continue reading until the end of the file
|
||||
while(! feof($f)) {
|
||||
$buffer .= fread($f, $len);
|
||||
}
|
||||
|
||||
fclose($f);
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
}
|
||||
29
src/Renderer.php
Normal file
29
src/Renderer.php
Normal file
@@ -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]));
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
ob_clean();
|
||||
flush();
|
||||
|
||||
$formatter = Formatter::getFormatter();
|
||||
$formatter->render($data);
|
||||
}
|
||||
}
|
||||
105
src/WebService.php
Normal file
105
src/WebService.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace BSR\Lib;
|
||||
|
||||
use BSR\Lib\Exception\UsageException;
|
||||
use BSR\Lib\Exception\WebException;
|
||||
|
||||
abstract class WebService
|
||||
{
|
||||
private $func = null;
|
||||
private $status = 200;
|
||||
|
||||
private $version = null;
|
||||
|
||||
public function __construct($version) {
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Treat the current request and output the result. This is the only
|
||||
* method that should be called on the webservice directly !
|
||||
*/
|
||||
public function Run()
|
||||
{
|
||||
Logger::start(array('version' => $this->version));
|
||||
|
||||
$renderer = new Renderer();
|
||||
|
||||
$data = array();
|
||||
|
||||
try {
|
||||
$result = $this->Call();
|
||||
$data["result"][$this->func] = $result;
|
||||
|
||||
// Logger::log(print_r($result, true));
|
||||
} catch (WebException $e) {
|
||||
$data["error"]["code"] = $e->getCode();
|
||||
$data["error"]["reason"] = $e->getMessage();
|
||||
$data["error"]["name"] = $e->getName();
|
||||
|
||||
$data['extra'] = Help::exception($e, $this, $this->func);
|
||||
|
||||
$this->status = 400;
|
||||
|
||||
Logger::info($e->getName(), 'error');
|
||||
} catch (\Exception $e) {
|
||||
$data["failure"]["code"] = $e->getCode();
|
||||
$data["failure"]["reason"] = $e->getMessage();
|
||||
$this->status = 500;
|
||||
|
||||
Logger::info($e->getMessage(), 'error');
|
||||
}
|
||||
|
||||
Logger::stop(array('status' => $this->status));
|
||||
$renderer->render($this->status, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which method to call based on GET or POST parameters and
|
||||
* call it before returning the result.
|
||||
*
|
||||
* @return array
|
||||
* @throws UsageException
|
||||
*/
|
||||
private function Call()
|
||||
{
|
||||
session_save_path(Configuration::get('session.save_path'));
|
||||
session_start();
|
||||
|
||||
$params = empty($_GET) ? $_POST : $_GET;
|
||||
if (empty($params)) {
|
||||
throw new UsageException("NoArguments", "No arguments specified.", UsageException::NO_ARGS);
|
||||
}
|
||||
|
||||
if (!array_key_exists("func", $params)) {
|
||||
throw new UsageException("MissingMethod", "No method specified.", UsageException::MISSING_METHOD);
|
||||
}
|
||||
|
||||
$this->func = $params["func"];
|
||||
unset($params['func']);
|
||||
|
||||
Logger::info(array(
|
||||
'func' => $this->func.'('.implode(', ', $params).')',
|
||||
));
|
||||
|
||||
if (!is_callable(array($this, $this->func))) {
|
||||
throw new UsageException("BadMethod", "Method {$this->func} does not exists.", UsageException::BAD_METHOD);
|
||||
}
|
||||
|
||||
$rm = new \ReflectionMethod($this, $this->func);
|
||||
$nbParams = count($params);
|
||||
$nbArgsFix = $rm->getNumberOfRequiredParameters();
|
||||
$nbArgs = $rm->getNumberOfParameters();
|
||||
|
||||
/* Check the number of arguments. */
|
||||
if ($nbParams < $nbArgsFix) {
|
||||
throw new UsageException("TooFewArgs", "You must provide at least $nbArgsFix arguments.", UsageException::TOO_FEW_ARGS);
|
||||
}
|
||||
if ($nbParams > $nbArgs) {
|
||||
throw new UsageException("TooManyArgs", "You must provide at most $nbArgs arguments.", UsageException::TOO_MANY_ARGS);
|
||||
}
|
||||
|
||||
return call_user_func_array(array($this, $this->func), $params);
|
||||
}
|
||||
}
|
||||
18
src/autoloader.php
Normal file
18
src/autoloader.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
ini_set('display_startup_errors', 'On');
|
||||
ini_set('display_errors', 'On');
|
||||
date_default_timezone_set('Europe/Zurich');
|
||||
|
||||
// register an autoloader to automatically load classes
|
||||
// the namespace for the class must begin with BSR and
|
||||
// otherwise respect the PSR-4 standard
|
||||
spl_autoload_register(function ($class) {
|
||||
$class = substr($class, strlen('BSR'));
|
||||
$path = sprintf('%s/../%s.php', __DIR__, str_replace('\\', '/', $class));
|
||||
|
||||
if (file_exists($path)) {
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
require $path;
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user