commit d750252304070edafee61d9b251854a8a3529c7e Author: Geoff Doty Date: Sun Dec 9 03:04:11 2018 -0500 Initial commit, part due diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e56116 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/vendor/ +/build/ +composer.lock \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..051b8dd --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +# Mite +> The tiniest framework that *mite* work + +*A word of warning, this is an experiment that I'm resurrecting, so it's quite dated atm* + +--- + +``` + /___\ ___ _ + )O.o( |\/| o | |_ BE small + \_^_/ | | | | |_ be STRONG + " " +``` + +--- + +## Overview + +**Mite** is a single-file framework that was born out of a necessity of its time, circa 2011, to enable rapid prototyping for PHP. When ideas came too fast to capture in most conventional frameworks for PHP, **Mite** provided a one-include-and-go solution. + +This was also the first time I had the idea to break away from `MVC` structure and just build something based around routing. Turned out this idea was not mine alone, it started with `sinatra.rb`, and continued with `express.js`. + +There are **better options** out there now + +- [FlightPHP](http://flightphp.com/) appears to have achieved my goal + + +### Features + +### Requirements + +- PHP 5.3+ + + +## Usage + +Create an `index.php` file and put the following code in it + +```php + +class Prototype extends Mite { + + function index() + { + // *.tpl.* not required + $this->view('index.tpl.php'); + } +} + +$app = new Prototype(); +$app->route('/', 'index'); +$app->run(); +``` + +The file name as `index.php` is important for routing + + +> SEE: `examples/` + +## TODO + +- More Examples +- Document API +- Modernize (ditch 5.2), but remain small +- Mite DB construct take PDO object, not build one +- Support REST functionality \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..7207031 --- /dev/null +++ b/composer.json @@ -0,0 +1,24 @@ +{ + "name": "n2geoff/mite", + "description": "single-file php framework for rapid prototyping", + "type": "framework", + "license": "MIT", + "authors": [ + { + "name": "Geoff Doty", + "email": "n2geoff@gmail.com" + } + ], + "scripts": { + "test": "vendor/bin/phpunit" + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "require": {}, + "require-dev": { + "phpunit/phpunit": "^7.5" + } +} diff --git a/examples/53oop.php b/examples/53oop.php new file mode 100644 index 0000000..71a2593 --- /dev/null +++ b/examples/53oop.php @@ -0,0 +1,13 @@ + TRUE, 'index' => '/mite/examples/53oop.php')); + +$app->route('/', function() use($app) { + $app->view('views/index.tpl.php'); +}); + +$app->run(); \ No newline at end of file diff --git a/examples/extend.php b/examples/extend.php new file mode 100644 index 0000000..b2e79af --- /dev/null +++ b/examples/extend.php @@ -0,0 +1,16 @@ +view('views/index.tpl.php'); + } +} + +$app = new test(array('debug' => TRUE, 'index' => '/mite/examples/extend.php')); +$app->route('/', 'index'); +$app->run(); \ No newline at end of file diff --git a/examples/views/index.tpl.php b/examples/views/index.tpl.php new file mode 100644 index 0000000..8d9d97d --- /dev/null +++ b/examples/views/index.tpl.php @@ -0,0 +1,73 @@ + + + + Mite Work + + + + + +
+
+
+
+

MITE

+
+ ./___\.........___ _................................. + .)O.o(...|\/|.o.|.|_.....BE small....................
+ .\_^_/...|..|.|.|.|_.....be STRONG...................
+ .."."................................................ +
+
A Rapid Prototyping Micro Framework
+
+
+
+ +
+ MITE is a tiny php framework library designed + to enble developers to rapidly prototype out micro ideas. +

+ What are Micro Ideas? +

+ Micro Ideas are things like the developer logging tool + logr, or the simple issue tracker taskd +

+ And, just for fun;) +
+
+

Features

+
    +
  • Tiny (under 15k)
  • +
  • Portable (single file)
  • +
  • Dynamic Named Routes
  • +
  • Debugging Profiler
  • +
  • Includes a tiny CSS Library
  • +
+
+
+

Requirements

+
    +
  • PHP 5.2.x or beyond
  • +
+
+
+

Code!

+
+ include 'mite.php'; +
+
+ +
+
+
©2011 Mite. All rights reserved.
+
+
+
+ + +s \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..e1b92ad --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,15 @@ + + + + + tests + + + diff --git a/src/mite.php b/src/mite.php new file mode 100644 index 0000000..1a06090 --- /dev/null +++ b/src/mite.php @@ -0,0 +1,472 @@ + + * @version .76 + * + * @copyright 2011 Geoff Doty + * @license + */ +class Mite { + + static private $routes = array(); //route-to-class mapping + + static private $index = '/index.php'; // + + static private $request = NULL; //url in address bar + + // for classes + public $callback = NULL; //class/function + + static private $init = NULL; //ah, bad name + static public $errors = array(); //bites + + // unused + static private $method = NULL; //for RESTish + static private $options = array(); //Registry + + // initilizes framework + public function __construct($config = array()) + { + $this->init($config); + } + + public function init($config = array()) + { + // always set init (can be used as singleton later?) + self::$init = microtime(TRUE); + + // sensible defaults + $defaults = array + ( + 'index' => '/index.php', + 'debug' => 'FALSE', + 'paths' => array( + 'views' => __DIR__, + 'models' => __DIR__ + ) + ); + + // merge in configuration overriding defaults + self::$options = array_merge($defaults, $config); + + // TODO: ack!!! forgot to update/retrieve settings from options + self::$index = self::options('index'); + } + + /** + * Builds route array + * + * Maps urls to classes or functions + */ + public function route($url, $callback) + { + // for dynamic urls + $url = preg_replace(array("@\:name@", "@\:id@"), array('([a-zA-Z\_\-]+)','([0-9]+)'), $url); + + self::$routes[$url] = $callback; + } + + // TODO: delete/add profile (echo set options) + public function _options() + { + print_r(self::$options); + } + + // + public function run() + { + $request = self::uri(); + + foreach(self::$routes as $url => $callback) + { + preg_match("@^{$url}$@", $request, $matches); + + if(isset($matches[0]) && count($matches[0] > 0)) + { + if(is_callable($callback)) + { + return call_user_func($callback); + } + else + { + // $class = get_class($this); //5 + + // $class = get_called_class(); //5.3 + + // if(is_callable("{$class}::{$callback}")) + if(is_callable(array($this, $callback))) + { + // return call_user_func("{$class}::{$callback}"); //called staticly 5.2.3+ + // return call_user_func(array($class, $callback)); //called? + return $this->$callback(); + } + else + { + echo 'PROBLEM'; + } + // header("HTTP/1.1 404 Not Found"); + exit('Function Not Found'); + } + } + } + + // header("HTTP/1.1 404 Not Found"); + exit('Page Not Found'); + + } + + //TODO: index.php required in url should make it either or? hmm htaccess issue + public function uri($raw = FALSE) + { + // cleans and strips query string + // $uri = str_replace($this->controller, '', preg_replace('/\?.+/', '', $_SERVER['REQUEST_URI'])); + $uri = str_replace(self::$index, '', preg_replace('/\?.+/', '', $_SERVER['REQUEST_URI'])); + + return ($uri == '' OR $uri == '//') ? '/' : $uri; + } + + function options($key = NULL, $value = NULL) + { + if($key === NULL) + { + return self::$options; + } + else + { + if($value === NULL) + { + return $option = isset(self::$options[$key]) ? self::$options[$key] : NULL ; + } + else + { + return $option = isset(self::$options[$key][$value]) ? self::$options[$key][$value] : NULL ; + } + } + } + + /***************************************************************** + * State Management + *****************************************************************/ + /** + * Cookie Managment + * + * set/get cookies by call signature + */ + public function cookie($name, $value = NULL, $expire = 0, $path = '', $domain = '', $secure = FALSE, $httponly=false) + { + if($value === NULL) + { // read cookie + return $cookie = isset($_COOKIE[$name]) ? $_COOKIE[$name] : NULL ; + } + else + { // create cookie + setcookie($name, $value, $expire, $path, $domain, $secure, $httponly); + } + } + + /** + * Flash Notice + * + * Creates cookie expires on next request + */ + public function flash($name, $value) + { + self::cookie($name, $value, $expire = time()+1); + } + + public function session($key, $value = NULL) + { + if($value === NULL) + { // read session + return $session = isset($_SESSION[$name]) ? $_SESSION[$name] : NULL ; + } + else + { // create session + $_SESSION[$key] = $value; + } + } + + /***************************************************************** + * Helper Methods + *****************************************************************/ + + /** + * Return Uri Segment + */ + public function uri_segment($number) + { + $segments = explode('/', trim(self::$request, '/')); + + return $segment = isset($segments[($number - 1)]) ? $segments[($number - 1)] : NULL; + } + + public function input($key, $clean = TRUE) + { // merge global input arrays + $input = array_merge($_GET, $_POST); + + if($clean === TRUE) + { + return isset($input[$key]) ? self::xss($input[$key]) : NULL; + } + else + { + return isset($input[$key]) ? $input[$key] : NULL; + } + } + + public function xss($value) + { + $value = stripslashes($value); + return htmlentities($value); + } + + public function style($file = NULL) + { + if($file === NULL) + { + $style = "\n"; + } + + return $style; + } + + public function view($template, $data = NULL) + { + if($data != NULL) + { + if(is_array($data)) + { + extract($data); + } + } + + $dir = self::options('paths','views'); + + include("{$dir}/{$template}"); + } + + /***************************************************************** + * Error Handling and Logging + *****************************************************************/ + + public function errors() {} + protected function has_error() + { + if(count(self::$errors) > 0) + { + return TRUE; + } + + return FALSE; + } + public function log($message) {} + + /***************************************************************** + * Profiling + *****************************************************************/ + + function explain() + { + $output = "\n\n"; + $output .= Mite::style(); + + $output .= "
\n"; + $output .= "
\n\tDebugging Information\n"; + + // Routing + $output .= "
\n"; + $output .= "\t
PAGE REQUEST
\n"; + $output .= "\t
" . self::uri() . "
\n"; + $output .= "\t
 
\n"; + $output .= "
\n"; + + $output .= "
\n"; + foreach(self::$routes as $route => $callback) + { + $output .= "\t
 
\n"; // future request method + $output .= "\t
{$route}
\n"; + $output .= "\t
"; + $output .= is_object($callback) ? get_class($callback) : $callback; + $output .= "
\n"; + } + $output .= "
\n"; + + // Request varibles + $output .= "
GET DATA
\n"; + foreach($_GET as $key => $value) + { + $output .= "\t
{$key}
\n"; + $output .= "\t
{$value}
\n"; + } + + $output .= "
POST DATA
\n"; + foreach($_POST as $key => $value) + { + $output .= "\t
{$key}
\n"; + $output .= "\t
{$value}
\n"; + } + + $output .= "
COOKIE DATA
\n"; + foreach($_COOKIE as $key => $value) + { + $output .= "\t
{$key}
\n"; + $output .= "\t
{$value}
\n"; + } + + $output .= "
SESSION DATA
\n"; + if(isset($_SESSION)) + { + foreach((array) $_SESSION as $key => $value) + { + $output .= "\t
{$key}
\n"; + $output .= "\t
{$value}
\n"; + } + } + + // continued in __destruct() + return $output; + } + + public function __destruct() + { + if(self::options('debug') === TRUE) + { + $output = self::explain(); + + // Memory + $output .= "
MEMORY USAGE
\n"; + $output .= "\t
 
\n"; + $output .= "\t
" . round(memory_get_usage()/1024,2) . " KBs
\n"; + $output .= "\t
" . round(memory_get_peak_usage()/1024,2) . " KBs
\n"; + // $output .= "
\n"; + + // Profiling + $output .= "
PROFILING USAGE
\n"; + $output .= "\t
 
\n"; + $output .= "\t
" . round(microtime(true) - self::$init, 4) . " Seconds
\n"; + // $output .= "\n"; + + $output .= "\n\n"; + $output .= "\n\n"; + + echo $output; + } + } +} + +// Basic CRUD class, may remove, or make more query builder style +abstract class Mite_Model { + + protected $_table; // table name + protected $_key; // primary key + private $db; + + public function __construct($dsn, $username = NULL, $password = NULL) + { + $this->db = $this->connect($dsn, $username, $password); + } + + public function connect($dsn, $username = NULL, $password = NULL) + { + // $dsn = "{$driver}:dbname={$database};host={$host}"; + + try + { + return new PDO($dsn, $username, $password); + } + catch (PDOException $e) + { + throw new Exception($e->getMessage()); + } + } + + // basic crud + public function insert($data) + { + $sql = "INSERT INTO {$this->_table} "; + + $columns = array(); + $values = array(); + foreach($data as $column => $value) + { + $columns[] = $column; + $values[] = $value; + } + + $sql .= '(' . implode(',', $columns) . ') '; + $sql .= 'VALUES(' . implode(',', $values) . ')'; + + return $this->db->exec($sql); + } + + public function update($data) + { + $sql = "UPDATE {$this->_table} SET "; + + $updates = array(); + foreach($data as $column => $value) + { + $updates[] = "{$column}='{$value}'"; + } + + $sql .= implode(',', $updates); + $sql .= "WHERE {$this->_key}='{$value[$this->_key]}'"; + + return $this->db->exec($sql); + } + + public function delete($data) + { + $sql = "DELETE FROM {$this->_table} "; + + $deletes = array(); + foreach($data as $column => $value) + { + $deletes[] = "{$column}='{$value}'"; + } + + $sql .= implode(',', $deletes); + $sql .= "WHERE {$column}='{$value}'"; + + return $this->db->exec($sql); + } + + public function find($value) + { + $sql = "SELECT * FROM {$this->_table} WHERE {$this->_key} = '{$value}'"; + return $this->db->query($sql); + } + + // raw sql + public function query($sql) + { + return $this->db->query($sql); + } + + public function last_id() + { + try + { + return $this->db->lastInsertId(); + } + catch(PDOException $e) + { + return NULL; + } + } +} diff --git a/tests/MiteTest.php b/tests/MiteTest.php new file mode 100644 index 0000000..8f2fa5c --- /dev/null +++ b/tests/MiteTest.php @@ -0,0 +1,12 @@ +assertInstanceOf('Mite', $mite); + } +}