Browse Source

Initial commit

Nathaniel van Diepen 8 years ago
commit
ebab176f24
9 changed files with 665 additions and 0 deletions
  1. 100 0
      app.class.php
  2. 31 0
      arguments.class.php
  3. 45 0
      base.interface.php
  4. 41 0
      dypath.class.php
  5. 5 0
      events.interface.php
  6. 54 0
      path.class.php
  7. 113 0
      response.class.php
  8. 92 0
      router.class.php
  9. 184 0
      sql.class.php

+ 100 - 0
app.class.php

@@ -0,0 +1,100 @@
+<?php
+	require_once('base.class.php');
+	require_once('router.class.php');
+	require_once('events.class.php');
+	class App implements Base, Events {
+		private static $apps = array();
+		private $routers;
+		public function __construct($name){
+			$this->name = $name;
+			$this->routers = array();
+			static::$apps[] $this;
+		}
+		public function __destruct(){
+			$this->routers = array();
+			$index = array_search(static::$apps, $this);
+			if($index !== false){
+				array_splice(static::$apps, $index, 1);
+			}
+			if(is_callable('parent::__destruct')){
+				parent::__destruct();
+			}
+		}
+		public static function shutdown(){
+			$verb = $_SERVER['REQUEST_METHOD'];
+			$url = parse_url($_SERVER['REDIRECT_URL']);
+			$data = file_get_contents( 'php://input','r');
+			foreach(static::$apps as $k => $app){
+				if($app instanceof App){
+					$app->handle($verb, $url, $data);
+				}
+			}
+			if(is_callable('parent::__destruct')){
+				parent::__destruct();
+			}
+		}
+		public static function shutdown_error($error){
+			foreach(static::$apps as $k => $app){
+				$app->error($error);
+			}
+		}
+		public function handle($verb, $url, $data){
+
+		}
+		public function error($error){
+
+		}
+		public function get_router($type, $path){
+
+		}
+		public function create_router($type, $path){
+			$router = new Router();
+			if(!isset($this->routers[$type]){
+				$this->routers[$type] = array();
+			}
+			$this->routers[$type][] = $router;
+			return $router;
+		}
+		public function route($type, $path, Callable $fn){
+			$router = $this->get_router($type, $prefix);
+			if($router == false){
+				$router = $this->create_router($type, $prefix);
+			}
+			$fn($router);
+		}
+		public function prefix($prefix, Callable $fn){
+			$this->route('prefix', $prefix, function($router){
+				$router->base($prefix);
+				$fn($router);
+			});
+		}
+	}
+	error_reporting(E_ALL);
+	ini_set('display_errors', 'On');
+	register_shutdown_function(function(){
+		App::shutdown();
+	});
+	set_error_handler(function($errno, $errstr, $errfile, $errline){
+		App::shutdown_error(array(
+			'number'=> $errno,
+			'msg'=> $errstr,
+			'file'=> $errfile,
+			'line'=> $errline,
+			'backtrace'=> debug_backtrace(),
+			'included'=> get_included_files()
+		));
+	},E_ALL);
+	register_shutdown_function(function(){
+		$error = error_get_last();
+		if($error['type'] == 1){
+			App::shutdown_error(array(
+				'number'=> $error['type'],
+				'msg'=> $error['message'],
+				'file'=> $error['file'],
+				'line'=> $error['line'],
+				'backtrace'=> array(),
+				'included'=> get_included_files()
+			));
+		}
+	});
+?>

+ 31 - 0
arguments.class.php

@@ -0,0 +1,31 @@
+<?php
+	class Arguments implements JsonSerializable, ArrayAccess{
+		public $args;
+		public function __construct($args){
+			$this->args = $args;
+		}
+		public function jsonSerialize(){
+			return $this->args;
+		}
+		public function __toString(){
+			return $this->path;
+		}
+		public function __get($name){
+			if(isset($this->args[$name])){
+				return $this->args[$name];
+			}
+		}
+		public function offsetGet($key){
+			return $this->args[$key];
+		}
+		public function offsetExists($key){
+			return isset($this->args[$key]);
+		}
+		public function offsetSet($key,$val){
+			$this->args[$key] = $val;
+		}
+		public function offsetUnset($key){
+			unset($this->args[$key]);
+		}
+	}
+?>

+ 45 - 0
base.interface.php

@@ -0,0 +1,45 @@
+<?php
+	interface Base extends JsonSerializable{
+		private $data;
+		public function __construct(){
+			$this->data = array();
+			// Constructor
+		}
+		public function __clone(){
+			// Clone handler
+		}
+		public function __destruct(){
+			// Destroy handler
+			$this->data = array();
+		}
+		public function __toString(){
+			$name = get_class($this);
+			if(!$name){
+				$name = "Object";
+			}
+			return "[".$name."]";
+		}
+		public function __invoke($res,$args){
+			// Invoke handler
+		}
+		public function jsonSerialize(){
+			return $this;
+		}
+		public function __get($name){
+			if(isset($this->data[$name])){
+				return $this->data[$name];
+			}
+		}
+		public function __set($name,$value){
+			if(isset($this->data[$name])){
+				$this->data[$name] = $value;
+			}
+		}
+		public function __isset($name){
+			return isset($this->data[$name]);
+		}
+		public function __unset($name){
+			unset($this->data[$name]);
+		}
+	}
+?>

+ 41 - 0
dypath.class.php

@@ -0,0 +1,41 @@
+<?php
+	require_once('arguments.class.php');
+	class DyPath implements JsonSerializable{
+		public $path;
+		public function __construct($path){
+			$this->path = $path;
+		}
+		public function jsonSerialize(){
+			return array(
+				'path'=>$this->path,
+				'regex'=>$this->regex,
+				'arguments'=>$this->arguments
+			);
+		}
+		public function __toString(){
+			return $this->path;
+		}
+		public function __get($name){
+			switch($name){
+				case 'arguments':
+					preg_match_all('/\{([^#\/][^}\n]+?)\}/',$this->path,$m,PREG_SET_ORDER);
+					return $m;
+				break;
+				case 'regex':
+					return '/^'.preg_replace('/\\\{[^#\/][^}\n]+?\\\}/','([^\/]*)',preg_quote($this->path,'/')).'$/';
+				break;
+			}
+		}
+		public function matches($url){
+			return preg_match($this->regex,$url);
+		}
+		public function args($url){
+			$ret = array();
+			preg_match_all($this->regex,$url,$m,PREG_SET_ORDER);
+			foreach($this->arguments as $k => $arg){
+				$ret[$arg[1]] = $m[0][$k+1];
+			}
+			return new Arguments($ret);
+		}
+	}
+?>

+ 5 - 0
events.interface.php

@@ -0,0 +1,5 @@
+<?php
+	interface Events {
+		
+	}
+?>

+ 54 - 0
path.class.php

@@ -0,0 +1,54 @@
+<?php
+	require_once('dypath.class.php');
+	class Path implements JsonSerializable{
+		private $handles = array();
+		public $path;
+		public function __construct($path){
+			$this->path = new DyPath($path);
+		}
+		public function __invoke($res,$args){
+			$err = null;
+			foreach($this->handles as $k => $fn){
+				try{
+					$fn($res,$args,$err);
+				}catch(Exception $e){
+					$err = $e;
+				}
+			}
+		}
+		public function __clone(){
+			// No cloning for now
+		}
+		public function __destruct(){
+			// Nothing to do here
+		}
+		public function jsonSerialize(){
+			return array(
+				'dypath'=>$this->path
+			);
+		}
+		public function __toString(){
+			return "[Path {$this->path}]";
+		}
+		public function __get($name){
+			switch($name){
+				
+			}
+		}
+		public function __set($name,$value){
+			switch($name){
+				
+			}
+		}
+		public function handle(Callable $fn){
+			array_push($this->handles,$fn);
+			return $this;
+		}
+		public function matches($path){
+			return $this->path->matches($path);
+		}
+		public function args($path){
+			return $this->path->args($path);
+		}
+	}
+?>

+ 113 - 0
response.class.php

@@ -0,0 +1,113 @@
+<?php
+	class Response {
+		public $output = '';
+		public $body = '';
+		public $url;
+		private $code = 200;
+		public $headers = array();
+		protected $open = true;
+		public function __construct($url){
+			$this->url = $url;
+		}
+		public function __toString(){
+			return $this->body;
+		}
+		public function clear(){
+			if($this->open){
+				$this->body = '';
+			}
+			return $this;
+		}
+		public function clear_headers(){
+			if($this->open){
+				$this->headers = array();
+			}
+			return $this;
+		}
+		public function clear_header($name){
+			foreach($this->headers as $key => $header){
+				if($header[0] == $name){
+					array_splice($this->headers, $key, 1);
+				}
+			}
+			return $this;
+		}
+		public function write($chunk){
+			if($this->open){
+				$this->body .= $chunk;
+			}
+			return $this;
+		}
+		public function json($json){
+			if(is_array($json)){
+				array_walk_recursive($json, function(&$item, $key){
+					if(!mb_detect_encoding($item, 'utf-8', true)){
+						$item = utf8_encode($item);
+					}
+				});
+			}
+			$this->write(json_encode($json));
+			if(json_last_error() != JSON_ERROR_NONE){
+				throw new Exception(json_last_error_msg());
+			}
+			return $this;
+		}
+		public function header($name,$value){
+			if($this->open){
+				array_push(
+					$this->headers,
+					array(
+						$name,
+						$value
+					)
+				);
+			}
+			return $this;
+		}
+		public function redirect($url){
+			$this->header('Location',Router::url($url));
+			return $this;
+		}
+		public function end($chunk=''){
+			if($this->open){
+				$this->write($chunk);
+				$this->open = false;
+			}
+			return $this;
+		}
+		public function img($img, $type = false){
+			if(!$type){
+				$type = $img->type;
+			}
+			$this->clear_header('Content-Type')
+				->header('Content-Type', 'image/'.$type);
+			if(!is_a($img, 'Image')){
+				$img = new Image(100, 20);
+				$img->text('Invalid Image',0,0,'black',12);
+			}
+			ob_start();
+			$img();
+			$this->write(ob_get_contents());
+			ob_end_clean();
+			return $this;
+		}
+		public function code($code=null){
+			if(is_null($code)){
+				return $this->code;
+			}
+			$this->code = $code;
+			return $this;
+		}
+		public function shutdown(){
+			if($this->open){
+				$this->end();
+			}
+			http_response_code($this->code);
+			foreach($this->headers as $k => $header){
+				header("{$header[0]}: $header[1]");
+			}
+			echo $this->body;
+			flush();
+		}
+	}
+?>

+ 92 - 0
router.class.php

@@ -0,0 +1,92 @@
+<?php
+	require_once('path.class.php');
+	require_once('response.class.php');
+	class Router {
+		private $_paths = array();
+		private $_base = '/';
+		private $responses = array();
+		public function __construct($base = null, $paths = null){
+			if($paths != null){
+				$this->paths($paths);
+			}
+			if($base != null){
+				$this->base($base);
+			}
+		}
+		public function __clone(){
+			// No cloning
+		}
+		public function __destruct(){
+			$this->_paths = array();
+		}
+		public function __toString(){
+			return "[Router]";
+		}
+		public function base($base){
+			$this->_base = $base;
+		}
+		public function url($url){
+			return preg_replace('/(\/+)/','/',$url);
+		}
+		// fn = function(response,args){}
+		public function path($path,$fn){
+			$obj = false;
+			foreach($this->_paths as $k => $p){
+				if($p->path == $path){
+					$obj = $p;
+				}
+			}
+			if(!$obj){
+				$obj = new Path($path);
+				array_push($this->_paths,$obj);
+			}
+			return $obj->handle($fn);
+		}
+		public function paths($paths){
+			foreach($paths as $path => $fn){
+				pthis->ath($path,$fn);
+			}
+		}
+		public function clear(){
+			$this->_paths = array();
+		}
+		public function handle($url,$res = null,$fn = null,$onerror = null){
+			if(strpos($url,$this->_base) !== false){
+				$url = substr($url,strpos($url,$this->_base)+strlen($this->_base));
+				if($url[0] != '/'){
+					$url = '/'.$url;
+				}
+				if(is_null($res)){
+					$res = new Response($url);
+				}else{
+					$res->url = $url;
+				}
+				if(!in_array($res,$this->responses)){
+					array_push($this->responses,$res);
+				}
+				ob_start();
+				$handled = false;
+				foreach($this->_paths as $k => $p){
+					if($p->matches($url)){
+						$handled = true;
+						try{
+							$p($res,$p->args($url));
+						}catch(Exception $e){
+							if(!is_null($onerror)){
+								$onerror($res,$e);
+							}else{
+								throw $e;
+							}
+						}
+					}
+				}
+				if(!$handled && !is_null($fn)){
+					$fn($res,$url);
+				}
+				$res->output = ob_get_contents();
+				ob_end_clean();
+			}
+			return $res;
+		}
+	}
+?>

+ 184 - 0
sql.class.php

@@ -0,0 +1,184 @@
+<?php
+	/**
+	* SQL class. Used for handling SQL connections
+	*
+	* @module sql
+	* @class SQL
+	* @constructor
+	*/
+	class SQL {
+		/**
+		* This is the mysqli connection beneath everything
+		* 
+		* @property sql
+		* @type {mysqli}
+		* @private
+		* @required
+		*/
+		private $sql;
+		public function __construct($server,$user,$pass,$db){
+			$this->sql = new mysqli($server,$user,$pass,$db) or die('Unable to connect to mysql');
+		}
+		public function __invoke(){
+			return $this->sql;
+		}
+		public function __get($name){
+			switch($name){
+				case 'error':
+					return $this->sql->error;
+				break;
+			}
+		}
+		/**
+		* Returns a Query object based on inputs
+		*
+		* @method query
+		* @param {String} sql The sql expression to run
+		* @param {String=null} [types] A string containing all the types of arguments being passed
+		* @param {Mixed} [bindings]* The bindings to use in the sql statement
+		* @return {Query} Returns the query object
+		*/
+		public function query(){
+			$reflect = new ReflectionClass('Query');
+			$args = array_merge(array($this),func_get_args());
+			return $reflect->newInstanceArgs($args);
+		}
+		public function escape($s){
+			return $this->sql->escape_string($s);
+		}
+		public function charset($charset){
+			return $this->sql->set_charset($charset);
+		}
+	}
+	/**
+	* Query class. Returned by SQL::query()
+	*
+	* @class Query
+	* @constructor
+	*/
+	class Query {
+		private $query;
+		private $sql;
+		public function __construct($sql,$source,$types=null){
+			$args = func_get_args();
+			$args = array_splice($args,2);
+			$this->sql = $sql();
+			$this->query = $sql()->prepare($source);
+			if(!is_null($types)){
+				call_user_func_array(array($this->query, 'bind_param'),make_referenced($args)) or die($sql()->error);
+			}
+		}
+		public function __invoke(){
+			return $this->query;
+		}
+		public function execute(){
+			if($this->query){
+				$r = $this->query->execute();
+				$this->sql->commit();
+				return $r;
+			}else{
+				return false;
+			}
+		}
+		public function __get($name){
+			switch($name){
+				/**
+				* Returns the mysqli::results object for the
+				* query
+				* 
+				* @property results
+				* @type {mysqli::results}
+				* @public
+				*/
+				case 'results':
+					if($this->query){
+						$this->execute();
+						$result = $this->query->get_result();
+						$this->query->close();
+						return $result;
+					}else{
+						return false;
+					}
+				break;
+				/**
+				* Returns an associative array of the query resulsts
+				* 
+				* @property assoc_results
+				* @type {Array}
+				* @public
+				*/
+				/**
+				* Returns an associative array of the query resulsts
+				* 
+				* @property resulsts_assoc
+				* @type {Array}
+				* @public
+				*/
+				case 'assoc_results':case 'results_assoc':
+					if($this->query){
+						$a = array();
+						$r = $this->results;
+						while($row = $r->fetch_assoc()){
+							array_push($a,$row);
+						}
+						return $a;
+					}else{
+						return false;
+					}
+				break;
+				/**
+				* Returns a numbered array of the query results
+				* 
+				* @property num_results
+				* @type {Array}
+				* @public
+				*/
+				/**
+				* Returns a numbered array of the query results
+				* 
+				* @property resulsts_num
+				* @type {Array}
+				* @public
+				*/
+				case 'num_results':case 'results_num':
+					if($this->query){
+						$a = array();
+						$r = $this->results;
+						while($row = $r->fetch_num()){
+							array_push($a,$row);
+						}
+						return $a;
+					}else{
+						return false;
+					}
+				break;
+				case 'assoc_result':case 'result_assoc':
+					if($this->query){
+						$r = $this->results;
+						return $r?$r->fetch_assoc():false;
+					}else{
+						return false;
+					}
+				break;
+				case 'num_result':case 'result_num':
+					if($this->query){
+						$r = $this->results;
+						return $r?$r->fetch_num():false;
+					}else{
+						return false;
+					}
+				break;
+				case 'insert_id':
+					return $this->sql->insert_id;
+				break;
+			}
+		}
+	}
+	function make_referenced(&$arr){
+		$refs = array();
+		foreach($arr as $key => $value){
+			$refs[$key] = &$arr[$key];
+		}
+		return $refs;
+	}
+?>