Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
vendor/
composer.lock
7 changes: 7 additions & 0 deletions .htaccess
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)$ index.php?url=$1 [QSA,NC,L]
# RewriteRule api/(.*)$ api/api.php?url=$1 [QSA,NC,L]
</IfModule>
33 changes: 33 additions & 0 deletions _bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

if(!defined('ROOT_DIR'))
define('ROOT_DIR', dirname(__FILE__));

if(!defined('VENDOR_DIR'))
define('VENDOR_DIR', ROOT_DIR . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR);

if(!defined('API_JWT_SECRET'))
define('API_JWT_SECRET', '12121980kcb');

// Composer
require VENDOR_DIR . 'autoload.php';

function recursive_autoloader($class, $path=ROOT_DIR)
{
$di = new DirectoryIterator($path);

foreach($di as $item) {
if($item->isDot())
continue;

if($item->isDir())
recursive_autoloader($class, $item->getPathname() . DIRECTORY_SEPARATOR);
}

$class_file = $path . "{$class}.class.php";

if(file_exists($class_file)) {
require_once $class_file;
}
}
spl_autoload_register('recursive_autoloader');
142 changes: 142 additions & 0 deletions class/DB.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php

class DB
{
private static $Conn;

private static function getInstance()
{
if(!self::$Conn) {
self::$Conn = new PDO('sqlite:db.sq3');
self::$Conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
self::$Conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}

return self::$Conn;
}

public static function getAllFrom($table, array $fields)
{
$fields = implode(', ', $fields);

$sql = "SELECT {$fields} FROM {$table}";

$rs = self::getInstance()->query($sql);

$output = array();
if($rs) {
$output = $rs->fetchAll();
}

return $output;
}

public static function getOneByIdFrom($table, array $fields, $id)
{
$fields = implode(', ', $fields);

$sql = "SELECT {$fields} FROM {$table} WHERE id = {$id}";

$rs = self::getInstance()->query($sql);
$item = $rs->fetch();

if(!$item)
return array();

return $item;
}

public static function getOneByField($table, array $fields)
{
$clauses = array();
$values = array();
foreach($fields as $field => $value) {
$clauses[] = "{$field} = ?";
$values[] = $value;
}
$clauses = implode(' AND ', $clauses);

$sql = "SELECT * FROM {$table} WHERE {$clauses}";
$st = self::getInstance()->prepare($sql);
$st->execute($values);

if(!$st)
return false;

return $st->fetch();
}

public static function saveAt($table, array $data)
{
if(!$data['id'])
return self::insert($table, $data);

return self::update($table, $data);
}

public function removeFrom($table, array $data)
{
$sql = "DELETE FROM {$table} WHERE id = ?";

$st = self::getInstance()->prepare($sql);
$st->execute(array($data['id']));

return $data;
}

private function insert($table, array $data)
{
$fields = array();
$values = array();

foreach($data as $field => $value) {
if(in_array($field, array('id')))
continue;

$fields[] = $field;
$values[] = $value;

$placeholders[] = '?';
}

$fields = implode(', ', $fields);
$placeholders = implode(', ', $placeholders);

$sql = "INSERT INTO {$table} ({$fields}) VALUES ({$placeholders})";
$st = self::getInstance()->prepare($sql);
$st->execute($values);

$data['id'] = self::getInstance()->lastInsertId();

return $data;
}

private function update($table, array $data)
{
$clauses = array();
$values = array();

foreach($data as $field => $value) {
if(in_array($field, array('id')))
continue;

$clauses[] = "{$field} = ?";
$values[] = $value;
}

$clauses = implode(', ', $clauses);

$sql = "UPDATE {$table} SET {$clauses} WHERE id = {$data['id']}";
$st = self::getInstance()->prepare($sql);
$st->execute($values);

return $data;
}

public function saveAuthTokenFor($table, $id, $auth_token)
{
$sql = "UPDATE {$table} SET auth_token = ? WHERE id = ?";
$st = self::getInstance()->prepare($sql);
$st->execute(array($auth_token, $id));
}
}
98 changes: 98 additions & 0 deletions class/Model.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

abstract class Model
{
protected static $table;
protected static $fields;

public function __construct($Request)
{
switch($Request->getVerb()) {
case 'GET':
if($Request->getId()) {
if(!is_numeric($Request->getId())) {
$Request->sendResponse(400);
}

$resource = $this->getById($Request->getId());

if(!$resource)
$Request->sendResponse(404);

$Request->sendResponse(200, $resource);
} else {
$resources = $this->getAll();

$Request->sendResponse(200, $resources);
}
break;

case 'POST':
if($Request->getId()) {
$Request->sendResponse(400);
}

$this->createFrom($Request);
break;

case 'PATCH':
if(!$Request->getId() || !is_numeric($Request->getId())) {
$Request->sendResponse(400);
}

$this->updateFrom($Request);
break;

case 'DELETE':
if(!$Request->getId() || !is_numeric($Request->getId())) {
$Request->sendResponse(400);
}

$this->deleteFrom($Request);
break;

default:
$Request->sendResponse(404);
}
}

protected function getAll()
{
$rs = DB::getAllFrom(static::getTable(), static::getFields());

if(!$rs)
return array();

return $rs;
}

protected function getById($id)
{
return DB::getOneByIdFrom(static::getTable(), static::getFields(), $id);
}

protected function generateAuthToken($id, $name, $email)
{
// Token data
$token_data = array(
'id' => $id,
'name' => $name,
'email' => $email
);
$auth_token = JWT::encode($token_data, API_JWT_SECRET);
DB::saveAuthTokenFor(Users::getTable(), $id, $auth_token);

return $auth_token;
}

// GETTERS
public static function getTable()
{
return static::$table;
}

public static function getFields()
{
return static::$fields;
}
}
110 changes: 110 additions & 0 deletions class/Request.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

class Request
{
private static $valid_endpoints = array('resources', 'users', 'login');

private $endpoint;
private $id;
private $verb;

public function __construct()
{
$this->verb = $_SERVER['REQUEST_METHOD'];
}

public function handle()
{

// Receives only JSON content
if(array_key_exists('CONTENT_TYPE', $_SERVER) && $_SERVER['CONTENT_TYPE'] !== 'application/json') {
$this->sendResponse(400);
}

$this->resolveUrl();
$this->validateAuthToken();

if(!in_array($this->getEndpoint(), self::$valid_endpoints))
$this->sendResponse(400);

// Create object and manipulate response
$class = ucwords($this->endpoint);
new $class($this);
}

public function sendResponse($status_code, $response_body=null, $additional_headers=array())
{
header("HTTP/1.0 {$status_code}", true, $status_code);
foreach ($additional_headers as $header) {
header($header);
}

if($response_body)
echo json_encode($response_body);

die;
}

private function resolveUrl()
{
if(!array_key_exists('url', $_GET)) {
$this->sendResponse(404);
}

$pieces = explode('/', $_GET['url']);

if(count($pieces) > 2) {
$this->sendResponse(400);
}

$this->endpoint = $pieces[0];
if(array_key_exists(1, $pieces)) {
$this->id = $pieces[1];
}
}

private function validateAuthToken()
{
// Set bypass authentication
if(($this->getEndpoint() === 'users' && !$this->getId()) || $this->getEndpoint() === 'login') {
return;
}

if(!array_key_exists('HTTP_X_AUTH', $_SERVER))
$this->sendResponse(401);

$auth_token = $_SERVER['HTTP_X_AUTH'];
try{
$token_data = JWT::decode($auth_token, API_JWT_SECRET, array('HS256'));
} catch(Exception $e) {
$this->sendResponse(401);
}

$fields = array(
'name' => $token_data->name,
'email' => $token_data->email,
'auth_token' => $auth_token,
);
$resource = DB::getOneByField(Users::getTable(), $fields);

if(!$resource)
$this->sendResponse(401);
}


// GETTERS
public function getEndpoint()
{
return $this->endpoint;
}

public function getId()
{
return $this->id;
}

public function getVerb()
{
return $this->verb;
}
}
Loading