Files
5ducky/lib/api.php
T
2022-10-06 22:04:13 +02:00

650 lines
18 KiB
PHP

<?php
/**
* Generate a simple api
*
* @author Daniel Leitner
* @version 1.2
* @link mail@danielleitner.de
*/
class Api {
/**
* Mysql connection
*
* @var resource
* @access private
*/
private $_db;
/**
* Store namcespaces
*
* @var array
* @access private
*/
private $_namespaces = array();
/**
* Working namespace name
*
* @var string
* @access private
*/
private $_current_namaspace;
/**
* Namespace which is rendered
*
* @var string
* @access private
*/
private $_render_namepsace;
/**
* Url data
*
* @var array
* @access private
*/
private $_route_data;
/**
* Class options
*
* @var array
* @access private
*/
private $_options = array(
'output_types' => array(
'json', 'xml', 'csv', 'yaml'
)
);
/**
* Rules to check the route
*
* @var array
* @access private
*/
private $_rules = array(
'alpha_numeric', 'numeric'
);
/**
* Select result for api
*
* @var array
*/
private $_result = array();
/**
* Let the magic start ;)
*
* @param resource $db
* @access public
*/
public function __construct($db) {
$this->_db = $db;
}
/**
* Register new namespace
*
* @param type $namespace
* @todo Set defaults to array
* @access public
* @return Api
*/
public function register($namespace) {
if( ! isset($this->_namespaces[$namespace]) ) {
$this->_namespaces[$namespace] = array(
'key' => false,
'sort' => false,
'request' => false,
'limit' => false,
'cache' => false,
'conditions' => false
);
$this->_current_namaspace = $namespace;
} else {
$this->_error('Namespace already exists.');
}
return $this;
}
/**
*
* @param string $message
* @access private
* @todo output
* @return Api
*/
private function _error($message) {
header("Content-Type: application/json");
die(json_encode($message));
}
/**
* Set table name
*
* @param string $table
* @return Api
*/
public function setTable($table) {
if( is_string($table) ) {
$this->_namespaces[$this->_current_namaspace]['table'] = $table;
} else {
$this->_error('Table name musst be a string.');
}
return $this;
}
/**
* Set output type
*
* @access public
* @param array|string $output
* @return Api
*/
public function setOutput($output) {
if( is_array($output) ) {
foreach($output as $o) {
if( ! in_array($o, $this->_options['output_types']) ) {
$this->_error('Output as '.$o.' is not supported.');
}
}
$this->_namespaces[$this->_current_namaspace]['output'] = $output;
} else {
if( ! in_array($output, $this->_options['output_types']) ) {
$this->_error('Output as '.$output.' is not supported.');
}
$this->_namespaces[$this->_current_namaspace]['output'] = $output;
}
return $this;
}
/**
* Set if unique key is required (false = default)
*
* @access public
* @param boolean $key
* @return Api
*/
public function setKey($key) {
if( is_bool($key) ) {
$this->_namespaces[$this->_current_namaspace]['key'] = $key;
}
return $this;
}
/**
* Set request limit per hour. (false = default)
*
* @access public
* @param integer $limit
* @return Api
*/
public function setLimit($limit) {
if( is_integer($limit) ) {
$this->_namespaces[$this->_current_namaspace]['limit'] = $limit;
}
return $this;
}
// Limit output
public function setRequest($request) {
if( is_integer($request) ) {
$this->_namespaces[$this->_current_namaspace]['request'] = $request;
}
return $this;
}
// Sort output
public function setSort($sort) {
if( is_string($sort) ) {
$this->_namespaces[$this->_current_namaspace]['sort'] = $sort;
}
return $this;
}
/**
* Activate cache
*
* @param integer $time
* @param string $folder
* @return Api
*/
public function setCache($time, $folder) {
if( ! is_dir($folder) ) {
$this->_error('Cache folder musst be a folder.');
}
if( ! is_integer($time) ) {
$this->_error('Cache time musst be integer.');
}
$this->_namespaces[$this->_current_namaspace]['cache'] = array(
'folder' => $folder,
'time' => $time
);
return $this;
}
/**
* Set columns which should be displayed
*
* @param array $columns
* @return Api
*/
public function setColumns($columns) {
if( is_array($columns) ) {
$this->_namespaces[$this->_current_namaspace]['columns'] = $columns;
} else {
$this->_error('Columns musst be given as array.');
}
return $this;
}
/**
* Set conditions for data
*
* @param array $conditions
* @return Api
*/
public function setWhere($conditions) {
if( is_array($conditions) ) {
$this->_namespaces[$this->_current_namaspace]['conditions'] = $conditions;
} else {
$this->_error('Where conditions musst be given as array.');
}
return $this;
}
/**
* Output data
*
* @access public
* @param arra $route
*/
public function render($route) {
if( ! isset($route['namespace']) || ! isset($this->_namespaces[$route['namespace']]) || strlen($route['namespace']) == 0 ) {
$this->_error('No namespace found.');
}
$this->_render_namepsace = $route['namespace'];
// secure input
foreach($route as $key => $value) {
$route[strip_tags($key)] = strip_tags($value);
}
$this->_route_data = $route;
// check if connection is working
if( ! mysql_ping($this->_db) ) {
$this->_error('Database connection is not working.');
}
// check if request limit is reached
if( ! $this->_check_key() ) {
$this->_error('Soory, but your api key is not valid.');
}
// check if request limit is reached
if( ! $this->_check_request() ) {
$this->_error('Soory, you reached the maximum request limit per hour ('.$this->_namespaces[$this->_render_namepsace]['limit'].').');
}
// get data from mysql table
if( ! $this->_create() ) {
$this->_error('Soory, but the system was not able to generate data.');
}
}
/**
* Check if the user key is valid
*
* @access private
* @return boolean
*/
private function _check_key() {
if( $this->_namespaces[$this->_render_namepsace]['key'] == true ) {
$result = mysql_query('SELECT id FROM api_keys WHERE hash = "'.mysql_real_escape_string($this->_route_data['key']).'"', $this->_db) or die(mysql_error());
if( mysql_num_rows($result) == false ) {
return false;
} else {
return true;
}
} else {
return true;
}
}
/**
* Check if request limit is reached
*
* @access prviate
* @return boolean
*/
private function _check_request() {
if( $this->_namespaces[$this->_render_namepsace]['limit'] != false ) {
// check request limit
// delete old requests
if( $this->_namespaces[$this->_render_namepsace]['key'] == true ) {
// request limit with key
// delete old requests
mysql_query('DELETE FROM api_requests WHERE hash = "'.mysql_real_escape_string($this->_route_data['key']).'" AND ((UNIX_TIMESTAMP() - time) > 3600) AND namespace = "'.$this->_render_namepsace.'"', $this->_db) or die(mysql_error());
// count stored requests
$result = mysql_query('SELECT COUNT(time) AS requests FROM api_requests WHERE hash = "'.mysql_real_escape_string($this->_route_data['key']).'" AND ((UNIX_TIMESTAMP() - time) < 3600) AND namespace = "'.$this->_render_namepsace.'"', $this->_db) or die(mysql_error());
$total_requests = mysql_fetch_array($result);
if( $total_requests['requests'] >= $this->_namespaces[$this->_render_namepsace]['limit'] ) {
// request limit reached
return false;
} else {
// insert new request
mysql_query('INSERT INTO api_requests (namespace, hash, ip, time) VALUES("'.$this->_render_namepsace.'", "'.mysql_real_escape_string($this->_route_data['key']).'", "'.$_SERVER['REMOTE_ADDR'].'", "'.time().'")', $this->_db) or die(mysql_error());
return true;
}
} else {
// request limit without key
// delete old requests
mysql_query('DELETE FROM api_requests WHERE ((UNIX_TIMESTAMP() - time) > 3600) AND namespace = "'.$this->_render_namepsace.'"', $this->_db) or die(mysql_error());
// count stored requests
$result = mysql_query('SELECT COUNT(time) AS requests FROM api_requests WHERE ((UNIX_TIMESTAMP() - time) < 3600) AND namespace = "'.$this->_render_namepsace.'"', $this->_db) or die(mysql_error());
$total_requests = mysql_fetch_array($result);
if( $total_requests['requests'] >= $this->_namespaces[$this->_render_namepsace]['limit'] ) {
// request limit reached
return false;
} else {
// insert new request
mysql_query('INSERT INTO api_requests (namespace, ip, time) VALUES("'.$this->_render_namepsace.'", "'.$_SERVER['REMOTE_ADDR'].'", "'.time().'")', $this->_db) or die(mysql_error());
return true;
}
}
} else {
// no limit is set
return true;
}
}
/**
* Array to json
*
* @access private
* @return json
*/
private function _write_json() {
header("Content-Type: application/json");
return json_encode($this->_result);
}
/**
* Array to xml
*
* @access private
* @return string
*/
private function _write_xml() {
header("Content-Type: application/xml");
$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
$xml .= "<root>";
foreach($this->_result as $key => $value) {
$xml .= "<{$this->_render_namepsace}>\n";
foreach($value as $k => $d) {
$xml .= "<{$k}>{$d}</{$k}>\n";
}
$xml .= "</{$this->_render_namepsace}>\n";
}
$xml .= "</root>";
return $xml;
}
/**
* Array to csv
*
* @access private
* @return string
*/
private function _write_csv() {
$lines = array();
foreach ($this->_result as $key => $value) {
$lines[] = implode(';', array_filter($value, function($var) {
return $var;
}));
}
return implode("\n", $lines);
}
/**
* Array to yaml
*
* @access private
* @return string
*/
private function _write_yaml() {
$yaml = "---\n";
$yaml .= "{$this->_render_namepsace}: \n";
foreach($this->_result as $key => $value) {
$yaml .= "-";
$count = 0;
foreach($value as $k => $d) {
if( $count != 0 ) {
$yaml .= " ";
}
$yaml .= " {$k}: {$d} \n";
$count++;
}
}
return $yaml;
}
/**
* Gets data from database
*
* @access private
*/
private function _get() {
foreach($this->_namespaces[$this->_render_namepsace]['columns'] as $c) {
$columns .= $c.', ';
}
$sql = 'SELECT '.substr($columns, 0, -2).' FROM ' .
$this->_namespaces[$this->_render_namepsace]['table'];
if( $this->_namespaces[$this->_render_namepsace]['conditions'] != false ) {
$sql .= ' WHERE ';
foreach($this->_namespaces[$this->_render_namepsace]['conditions'] as $where => $data) {
if( ! isset($this->_route_data[$data['route']]) ) {
$this->_error('Route '.$data['route'].' is required.');
}
// validate
if( isset($data['rule']) ) {
if( ! in_array($data['rule'], $this->_rules) ) {
$this->_error('Rule '.$data['rule'].' is not avaiable.');
}
$rule = '_'.$data['rule'];
if( ! $this->$rule($this->_route_data[$data['route']]) ) {
$this->_error('Wrong route format.');
}
}
$sql .= $where.' = "'.mysql_real_escape_string($this->_route_data[$data['route']]).'"';
if( isset($data['connect']) ) {
$sql .= ' '.$data['connect'];
}
}
}
if( $this->_namespaces[$this->_render_namepsace]['sort'] != false ) {
$sql .= ' ORDER BY ' . $this->_namespaces[$this->_render_namepsace]['sort'];
}
if( $this->_namespaces[$this->_render_namepsace]['request'] != false ) {
$sql .= ' LIMIT ' . $this->_namespaces[$this->_render_namepsace]['request'];
}
$result = mysql_query($sql, $this->_db) or die(mysql_error());
if( mysql_num_rows($result) == false ) {
$this->_error('No results found.');
}
// write data to a nice array
for($i = 0; $i < mysql_num_rows($result); $i++)
{
$dump[$i] = mysql_fetch_array($result, MYSQL_ASSOC);
}
$this->_result = $dump;
}
private function _alpha_numeric($validate) {
if( ctype_alnum($validate) ) {
return true;
} else {
return false;
}
}
private function _numeric($validate) {
if( ctype_digit($validate) ) {
return true;
} else {
return false;
}
}
private function _create() {
if( $this->_namespaces[$this->_render_namepsace]['cache'] == false ) {
$this->_get();
} else {
// cache is turned on
if( $this->_namespaces[$this->_render_namepsace]['key'] == false ) {
// key musst truned off
$file = $this->_namespaces[$this->_render_namepsace]['cache']['folder'].sha1($this->_render_namepsace).'.txt';
if( ! file_exists($file) || (time() - filemtime($file) > $this->_namespaces[$this->_render_namepsace]['cache']['time']) ) {
// write to cache and delete old file
@unlink($file);
$f = fopen($file, 'w');
$this->_get();
$cache = new stdClass;
$cache->result = $this->_result;
fwrite($f, serialize($cache));
fclose($f);
}
// read from cache
$data = unserialize(file_get_contents($file));
$this->_result = $data->result;
} else {
$this->_namespaces[$this->_render_namepsace]['cache'] = false;
$this->_create();
}
}
$type_function = '_write_';
if( is_array($this->_namespaces[$this->_render_namepsace]['output']) ) {
if( isset($this->_route_data['output']) ) {
$type_function .= $this->_route_data['output'];
} else {
$type_function .= $this->_namespaces[$this->_render_namepsace]['output'][0];
}
} else {
$type_function .= $this->_namespaces[$this->_render_namepsace]['output'];
}
die($this->$type_function());
}
}
?>