├── bootstrap.php ├── classes ├── controller.php ├── controller │ ├── home.php │ └── orm.php ├── cookie.php ├── db.php ├── model │ ├── .gitignore │ ├── group.php │ ├── groupuser.php │ └── user.php ├── orm.php ├── sql.php ├── stateful.php ├── time.php └── view.php ├── cli.php ├── config.sample.php ├── functions.php ├── index.php ├── license.txt ├── log └── .gitignore ├── readme.markdown ├── sample.nginx.conf └── views ├── 404.php ├── debug.php ├── error.php ├── exception.php ├── home ├── index.php └── param.php ├── layout.php └── orm └── index.php /bootstrap.php: -------------------------------------------------------------------------------- 1 | set($this);echo$l;} 6 | function show_404(){header("HTTP/1.0 404 Not Found");$this->content=new View('404');} 7 | } -------------------------------------------------------------------------------- /classes/controller/home.php: -------------------------------------------------------------------------------- 1 | content = new View('home/index'); 8 | } 9 | 10 | // Show how to pass parameters over the URI 11 | public function param($value1 = NULL, $value2 = NULL) 12 | { 13 | $view = new View('home/param'); 14 | 15 | $view->value1 = $value1; 16 | $view->value2 = $value2; 17 | // OR 18 | /* 19 | $view->set(array( 20 | 'value1' => $value1, 21 | 'value2' => $value2 22 | )); 23 | */ 24 | 25 | $this->content = $view; 26 | } 27 | 28 | // Example exception handling 29 | public function exception() 30 | { 31 | $this->bad_method('Some arguments here...'); 32 | } 33 | 34 | protected function bad_method($value) 35 | { 36 | throw new Exception('Not a flying toy!'); 37 | } 38 | 39 | // Example using cookies for session storage 40 | public function session() 41 | { 42 | // Start session 43 | $_SESSION = Cookie::get('session'); 44 | 45 | $this->content = dump($_SESSION); 46 | 47 | // Create a number counter 48 | if(isset($_SESSION['number'])) 49 | { 50 | $_SESSION['number'] += 1; 51 | } 52 | else 53 | { 54 | $_SESSION['number']=1; 55 | } 56 | 57 | // Create token (for use in forms) 58 | $_SESSION['token'] = Cookie::token(); 59 | 60 | // Save session 61 | Cookie::set('session',$_SESSION); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /classes/controller/orm.php: -------------------------------------------------------------------------------- 1 | groups = $group->get(); 15 | 16 | $this->content = $view; 17 | } 18 | 19 | } 20 | 21 | /* SQL for this looks like: 22 | 23 | 24 | CREATE TABLE IF NOT EXISTS `group` ( 25 | `id` int(11) NOT NULL AUTO_INCREMENT, 26 | `group` varchar(100) NOT NULL, 27 | PRIMARY KEY (`id`) 28 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ; 29 | 30 | INSERT INTO `group` (`id`, `group`) VALUES 31 | (1, 'Soccer'), 32 | (2, 'Chess'), 33 | (3, 'Photography'); 34 | 35 | -- -------------------------------------------------------- 36 | 37 | CREATE TABLE IF NOT EXISTS `group_user` ( 38 | `id` int(11) NOT NULL AUTO_INCREMENT, 39 | `user_id` int(11) NOT NULL, 40 | `group_id` int(11) NOT NULL, 41 | PRIMARY KEY (`id`) 42 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ; 43 | 44 | INSERT INTO `group_user` (`id`, `user_id`, `group_id`) VALUES 45 | (1, 1, 1), 46 | (2, 1, 2), 47 | (3, 1, 3), 48 | (4, 2, 3); 49 | 50 | -- -------------------------------------------------------- 51 | 52 | CREATE TABLE IF NOT EXISTS `user` ( 53 | `id` int(11) NOT NULL AUTO_INCREMENT, 54 | `name` varchar(100) NOT NULL, 55 | `email` varchar(100) NOT NULL, 56 | PRIMARY KEY (`id`) 57 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ; 58 | 59 | INSERT INTO `user` (`id`, `name`, `email`) VALUES 60 | (1, 'Bob', ''), 61 | (2, 'John', ''); 62 | */ -------------------------------------------------------------------------------- /classes/cookie.php: -------------------------------------------------------------------------------- 1 | pdo=new PDO($dsn,$user,$pass,$args);} 7 | function column($q,$p=NULL,$k=0){return($s=$this->query($q,$p))?$s->fetchColumn($k):0;} 8 | function row($q,$p=NULL){return($s=$this->query($q,$p))?$s->fetch(PDO::FETCH_OBJ):0;} 9 | function fetch($q,$p=NULL){return($s=$this->query($q,$p))?$s->fetchAll(PDO::FETCH_OBJ):0;} 10 | function query($q,$p=NULL){$s=$this->pdo->prepare(self::$q[]=str_replace('"',$this->i,$q));$s->execute($p);return$s;} 11 | } -------------------------------------------------------------------------------- /classes/model/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xeoncross/1kb-PHP-MVC-Framework/d838c3f00e07cf5d6e5f8f2caaf2b71cc5564752/classes/model/.gitignore -------------------------------------------------------------------------------- /classes/model/group.php: -------------------------------------------------------------------------------- 1 | 'model_groupuser' 10 | ); 11 | } -------------------------------------------------------------------------------- /classes/model/groupuser.php: -------------------------------------------------------------------------------- 1 | 'model_user' 9 | ); 10 | } -------------------------------------------------------------------------------- /classes/model/user.php: -------------------------------------------------------------------------------- 1 | 'model_groupuser' 10 | ); 11 | } -------------------------------------------------------------------------------- /classes/orm.php: -------------------------------------------------------------------------------- 1 | d[$t::$k]=$d;else{$t->d=(array)$d;$t->l=1;}}} 7 | function __get($k){$t=$this;$t->load();return($m=v($t::$r[$k]))?current($t->$k(array(),1)):(($m=v($t::$b[$k]))?new$m($t->d[$m::$f]):parent::__get($k));} 8 | function __call($m,$a){$t=$this;$m=new$t::$r[$m];$a=$a+array(1=>0,0,0);$a[0][$t::$f]=$t->{$t::$k};return$m->get($a[0],$a[1],$a[2],$a[3]);} 9 | function save(){$t=$this;if($d=$t->changes()){$k=$t::$k;return$t->l=empty($t->d[$k])?($t->$k=$t::$db->insert($t::$t,$d)):$t::$db->update($t::$t,$d,array($k=>$t->$k));}} 10 | function load(){$t=$this;if($t->l)return 1;$k=$t::$k;if(empty($t->d[$k]))return;if($r=$t->get(array($k=>$t->d[$k]))){$t->l=1;$t->c=0;return$t->d=$r[0]->values();}} 11 | function get($w=0,$l=0,$o=0,$s=0,$c=0,$f='fetch'){$t=$this;list($q,$p)=$t::$db->select($c,$t::$t,$w,$l,$o,$s);$r=$t::$db->$f($q,$p);if($f{0}=='f'){foreach($r as&$v)$v=new$t($v);}return$r;} 12 | function count($w=0){return$this::$db->count($this::$t,$w);} 13 | } -------------------------------------------------------------------------------- /classes/sql.php: -------------------------------------------------------------------------------- 1 | where($w);if($w)$q.=" WHERE $w";return($s=$this->query($q,$p))?$s->rowCount():0;} 6 | function select($c=0,$t,$w=0,$l=0,$o=0,$s=0){$c=$c?:'*';$q="SELECT $c FROM \"$t\"";list($w,$p)=$this->where($w);if($w)$q.=" WHERE $w";return array($q.($s?" ORDER BY $s":'').($l?" LIMIT $o,$l":''),$p);} 7 | function count($t,$w=0){list($q,$p)=$this->select('COUNT(*)',$t,$w);return$this->column($q,$p);} 8 | function insert($t,$d){$q="INSERT INTO $t (\"".implode('","',array_keys($d)).'")VALUES('.rtrim(str_repeat('?,',count($d)),',').')';return $this->query($q,array_values($d))?$this->pdo->lastInsertId():0;} 9 | function update($t,$d,$w=NULL){$q="UPDATE $t SET \"".implode('"=?,"',array_keys($d)).'"=? WHERE ';list($a,$b)=$this->where($w);return(($s=$this->query($q.$a,array_merge(array_values($d),$b)))?$s->rowCount():NULL);} 10 | function where($w=0){$a=$s=array();if($w){foreach($w as$c=>$v){if(is_int($c))$s[]=$v;else{$s[]="\"$c\"=?";$a[]=$v;}}}return array(join(' AND ',$s),$a);} 11 | } -------------------------------------------------------------------------------- /classes/stateful.php: -------------------------------------------------------------------------------- 1 | $v)$this->$k=$v;} 7 | function __set($k,$v){$t=$this;if(!array_key_exists($k,$t->d)OR$t->d[$k]!==$v){$t->d[$k]=$v;$t->c[$k]=$k;}} 8 | function __get($k){return array_key_exists($k,$this->d)?$this->d[$k]:NULL;} 9 | function __isset($k){return array_key_exists($k,$this->d);} 10 | function __unset($k){unset($this->d[$k],$this->c[$k]);} 11 | function clear(){$this->d=$this->c=array();} 12 | function values(){return$this->d;} 13 | function changed(){return(bool)$this->c;} 14 | function changes(){if($this->c){$a=array();foreach($this->c as$k)$a[$k]=$this->d[$k];return$a;}} 15 | } -------------------------------------------------------------------------------- /classes/time.php: -------------------------------------------------------------------------------- 1 | format('Y-m-d H:i:s');} 8 | function difference($d='NOW',$l=1){$d=$this->diff($d);$u=array('y'=>'year','m'=>'month','d'=>'day','h'=>'hour','i'=>'minute','s'=>'second');$r=array();foreach($u as$k=>$n){$v=$d->$k;if($v)$r[]="$v $n".($v>1?'s':'');if(count($r)==$l)return implode(',',$r);}} 9 | function humanFriendly($format='M j,Y \a\t g:ia'){$d=$this->diff();$t=$this->getTimestamp();if(!$d->d){$s=$this->difference();return$t>time()?"in $s":"$s ago";}return$this->format($format);} 10 | static function show($time){$t=new Time($time);return$t->humanFriendly();} 11 | } 12 | -------------------------------------------------------------------------------- /classes/view.php: -------------------------------------------------------------------------------- 1 | v=p("views/$v");} 6 | function set($a){foreach($a as$k=>$v)$this->$k=$v;} 7 | function __toString(){try {extract((array)$this);require$v;return'';} catch(\Exception $e){print_r(DB::$q); return"$e";}} 8 | } -------------------------------------------------------------------------------- /cli.php: -------------------------------------------------------------------------------- 1 | TRUE, 7 | 8 | // Database Settings 9 | 'db' => array( 10 | 'dsn' => 'mysql:host=localhost;port=3306;dbname=test', 11 | 'user' => 'root', 12 | 'pass' => '', 13 | 'args' => array( 14 | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 15 | //PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8" // If using MySQL, force UTF-8 16 | ) 17 | ), 18 | 19 | // Cookie options 20 | 'cookie' => array( 21 | 'key' => 'a.very.long.secret.key.here', 22 | 'expires' => time()+60*5, // 30 seconds 23 | 'path' => '/', 24 | 'domain' => '', 25 | 'secure' => '', 26 | 'httponly' => '', 27 | ) 28 | 29 | ); 30 | 31 | 32 | return $config; -------------------------------------------------------------------------------- /functions.php: -------------------------------------------------------------------------------- 1 | '.print_r($v,1).'';} 9 | function post($k,$d='',$s=1){$v=v($_POST[$k],$d);return($s&&is_string($v))?$v:(!$s&&is_array($v)?$v:$d);} 10 | function _log($m){file_put_contents(__DIR__.'/log/.'.date('Y-m-d'),time().' '.getenv('REMOTE_ADDR')." $m\n",FILE_APPEND);} 11 | function h($s){return htmlspecialchars($s,ENT_QUOTES,'utf-8');} 12 | function redirect($u='',$r=0,$c=302){header($r?"Refresh:0;url=$u":"Location: $u",TRUE,$c);} 13 | function registry($k,$v=null){static$o;return(func_num_args()>1?$o[$k]=$v:(isset($o[$k])?$o[$k]:NULL));} 14 | function utf8($s,$f='UTF-8'){return @iconv($f,$f,$s);} -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | e=$e;$v->f=$f;$v->l=$l;echo$v;_log("$e [$f:$l]");}); 9 | function exception($e){$v=new View('exception');$v->e=$e;_log($e->getMessage().' '.$e->getFile());die($v);} 10 | set_exception_handler('exception'); 11 | register_shutdown_function(function(){if($e=error_get_last())exception(new ErrorException($e['message'],$e['type'],0,v($e['file']),$e['line']));}); 12 | $c='controller_'.(url(0)?:'home');$m=url(1)?:'index';if(!is_file(p("classes/$c"))||!($c=new$c)||$m=='render'||!in_array($m,get_class_methods($c))){$c=new controller;$m='show_404';} 13 | call_user_func_array(array($c,$m),array_slice(url(),2)); 14 | $c->render(); -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 David Pennington 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /log/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xeoncross/1kb-PHP-MVC-Framework/d838c3f00e07cf5d6e5f8f2caaf2b71cc5564752/log/.gitignore -------------------------------------------------------------------------------- /readme.markdown: -------------------------------------------------------------------------------- 1 | ## 1kb PHP MVC 2 | 3 | A working 1kb PHP framework with additional 1kb classes if needed. To get started, copy config.sample.php to config.php and edit as needed. 4 | 5 | Connecting to a database is easy. 6 | 7 | $db = new DB(c('db')); 8 | 9 | All queries are prepared statements to prevent SQL injection. 10 | 11 | $statement = $db->query($sql, $params); 12 | 13 | You can also use the helper functions if you wish to request one or more results. 14 | 15 | $rows = $db->fetch($sql, $params); // All rows 16 | $row = $db->row($sql,$params); // Single row 17 | $column = $db->column($sql,$params,3); // Third column 18 | 19 | To use the ORM create classes that map to database tables. 20 | 21 | class Model_User extends ORM 22 | { 23 | static $t = 'user'; 24 | static $k = 'id'; 25 | static $f = 'user_id'; 26 | static $r = array( 27 | 'posts' => 'Model_Post', // Has many posts 28 | 'roles' => 'Model_Role', // Has many roles 29 | 'profile' => 'Model_Profile', // Has one profile 30 | ); 31 | static $b = array('roles' => 'Model_Role'); // Belongs to roles 32 | } 33 | 34 | You also need to set the database connection the ORM will use. 35 | 36 | ORM::$db = $db; 37 | //OR 38 | Model_User::$db = $db; 39 | 40 | From there you can use the $user->get() and $user->count() methods to interact with database results. For example, you could approve inactive users like this: 41 | 42 | $user = new Model_User; 43 | foreach($user->get(array('active'=>FALSE)) as $user) 44 | { 45 | if($user->paid) 46 | { 47 | $user->active = TRUE; 48 | $user->save(); 49 | } 50 | } 51 | 52 | Or you can fetch a users posts: 53 | 54 | $user = new Model_User; 55 | $posts = $user->posts(); 56 | 57 | You can create users just as easily. 58 | 59 | $user = new Model_User; 60 | $user->name = 'John'; 61 | $user->email = 'john@example.com'; 62 | $user->save(); 63 | 64 | Lots of other fun things can be done in only 1kb. 65 | Read the code man, read the code. 66 | 67 | [David Pennington](http://xeoncross.com/) 68 | Released under the MIT License -------------------------------------------------------------------------------- /sample.nginx.conf: -------------------------------------------------------------------------------- 1 | # Basic server setup 2 | server { 3 | listen 80; 4 | server_name 1kbmvc.loc; 5 | root /var/www/1kbmvc; 6 | index index.html index.php; 7 | try_files $uri @missing; 8 | 9 | location @missing { 10 | rewrite ^ /index.php$request_uri last; 11 | } 12 | 13 | # This will only run if the below location doesn't, so anything other than /index.php 14 | location ~ \.php { 15 | rewrite ^ /index.php$request_uri last; 16 | } 17 | 18 | # Pass index.php to PHP Fastcgi 19 | location ^~ /index.php { 20 | include fastcgi.conf; 21 | fastcgi_pass 127.0.0.1:9000; 22 | } 23 | } -------------------------------------------------------------------------------- /views/404.php: -------------------------------------------------------------------------------- 1 | 2 |

404 Not Found

3 |

Sorry, we could not find the page you requested.

4 | -------------------------------------------------------------------------------- /views/debug.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | Benchmarks 5 |
 6 |  ms
 7 |  bytes
 8 |  bytes (process)
 9 |  bytes (process peak)
10 | 
11 | 12 | URL 13 |
14 | 15 | '. count(DB::$q). ' Database Queries'; 18 | foreach(DB::$q as $query) 19 | { 20 | print '
'. $query. '
'; 21 | } 22 | } 23 | ?> 24 | 25 | 26 | Session Data 27 | ';print_r($_SESSION);print ''; ?> 28 | 29 | 30 | 31 | PHP Files Included: 32 |
33 | 
34 | 
35 | 
36 | 37 |
-------------------------------------------------------------------------------- /views/error.php: -------------------------------------------------------------------------------- 1 | 2 |

System Error

3 |

4 |

In on line

5 | -------------------------------------------------------------------------------- /views/exception.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Site Error 6 | 11 | 12 | 13 |
14 |

Site Error

15 |

getMessage(); ?>

16 | 17 | In '. str_replace(dirname(__DIR__),'',$e->getFile()).' on line '. $e->getLine(). '

'; 20 | //print dump($e->getTrace()); 21 | 22 | foreach($e->getTrace() as $item) 23 | { 24 | if(isset($item['file'])) $file = str_replace(dirname(__DIR__),'',$item['file']); 25 | $line = isset($item['line']) ? $item['line'] : ''; 26 | 27 | print $file. ($line?" on line $line":'').'
'; 28 | if(!empty($item['args'])) print dump($item['args']); 29 | } 30 | } 31 | ?> 32 |
33 | 34 | -------------------------------------------------------------------------------- /views/home/index.php: -------------------------------------------------------------------------------- 1 | 2 |

Welcome!

3 |

Thanks for trying out the 1kb MVC Framework!

-------------------------------------------------------------------------------- /views/home/param.php: -------------------------------------------------------------------------------- 1 | 2 |

URI Parameter Test

3 | 7 | /home/param/343/desc -------------------------------------------------------------------------------- /views/layout.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1kb MVC Framework 6 | 10 | 11 | 12 |
13 | 16 |
17 | 18 | 19 | 20 |
21 |
22 | 23 | -------------------------------------------------------------------------------- /views/orm/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | --------------------------------------------------------------------------------