aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.DS_Storebin0 -> 6148 bytes
-rw-r--r--App.php230
-rw-r--r--Controller.php368
-rw-r--r--Db.php98
4 files changed, 696 insertions, 0 deletions
diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..d150107
--- /dev/null
+++ b/.DS_Store
Binary files differ
diff --git a/App.php b/App.php
new file mode 100644
index 0000000..141e333
--- /dev/null
+++ b/App.php
@@ -0,0 +1,230 @@
+<?php
+
+ /**
+ * main/App.php
+ * @author Filipp Lepalaan <filipp@mekanisti.fi>
+ * @copyright 2009 Filipp Lepalaan
+ */
+
+class App
+{
+ /**
+ * Fire up the application
+ */
+ static public function init()
+ {
+ @list($controller, $param, $action) = App::url();
+
+ if (empty($param)) {
+ $action = "index";
+ }
+
+// $conf['basedir'] = dirname(dirname(__FILE__) . "/system");
+
+ if (!$controller) {
+ $controller = "user";
+ }
+
+ // Dispatch correct controller
+ $c = new $controller;
+
+ // Assume no method name was given, try $param, then default to defaultAction
+ // controller/param/action
+ if (method_exists($c, $action)) {
+ return $c->$action($c);
+ }
+
+ // controller/action
+ if (method_exists($c, $param)) {
+ return $c->$param($c);
+ }
+
+ // controller/param
+ if (method_exists($c, $c->defaultAction)) {
+ $action = $c->defaultAction;
+ return $c->$action($c);
+ }
+
+ exit(App::error("{$controller}_{$action}: no such method"));
+
+ }
+
+ static function param()
+ {
+ $url = App::url();
+ return $url[1];
+ }
+
+ // Requests should always be in the form: controller/action/parameters.type
+ // Strip type info since it's not needed at this point
+ static function url($index = null)
+ {
+ $req = ltrim($_SERVER['REQUEST_URI'], "/");
+ $array = explode("/", preg_replace('/\.\w+$/', '', $req));
+ return (is_numeric($index)) ? $array[$index] : $array;
+ }
+
+ static function conf($key = null)
+ {
+ $cpath = realpath("../system/config.ini");
+ $config = parse_ini_file($cpath, true);
+ $config = $config['development'];
+ return ($key) ? $config[$key] : $config;
+ }
+
+ static function type()
+ {
+ $last = array_pop(explode("/", @$_GET['url']));
+ $type = ltrim(strrchr($last, "."), ".");
+
+ $contentTypes = array('html', 'rss', 'xml', 'tpl', 'pdf', 'jpg');
+
+ if (in_array($type, $contentTypes)) {
+ return $type;
+ }
+
+ return "html";
+
+ }
+
+ static function ok($msg)
+ {
+ $ok = array('result' => 'ok', 'msg' => $msg);
+ header("Content-type application/json");
+ return json_encode($ok);
+ }
+
+ static function error($msg)
+ {
+ $err = array('result' => 'error', 'msg' => $msg);
+ header("Content-type application/json");
+ return json_encode($err);
+ }
+
+ /**
+ * Do a proper HTTP redirect
+ * @param string [$where] URL to redirect to
+ * @return void
+ */
+ static function redirect($url)
+ {
+ header("HTTP/1.1 303 See Other");
+ header("Location: $url");
+ }
+
+ static function locale()
+ {
+ // Set language to whatever the browser is set to
+ list($loc, $lang) = explode("-", $_SERVER['HTTP_ACCEPT_LANGUAGE']);
+ return sprintf("%s_%s", $loc, strtoupper($lang));
+ }
+
+ static function log($msg)
+ {
+ if (is_array($msg)) {
+ $msg = print_r($msg, true);
+ }
+ syslog(LOG_ERR, $msg);
+ }
+
+ public function delete($table, $where)
+ {
+ if (empty($where)) {
+ exit(App::error("Delete without parameters"));
+ }
+
+ list($key, $value) = each($where);
+ $sql = "DELETE FROM `$table` WHERE $key = :{$key}";
+
+ self::log($sql);
+ self::query($sql, $where);
+
+ }
+
+ /**
+ * Insert something in the database
+ */
+ static function insert($table, $data)
+ {
+ if (empty($data)) {
+ exit(self::error("Empty insert"));
+ }
+
+ $cols = array();
+ $vals = array();
+
+ foreach ($data as $k => $v) {
+ $cols[] = "`{$k}`";
+ $vals[] = ":{$k}";
+ }
+
+ $cols = implode(",", $cols);
+ $vals = implode(",", $vals);
+ $sql = "INSERT INTO `$table` ($cols) VALUES ($vals)";
+
+ self::log($sql);
+ self::query($sql, $data);
+
+ }
+
+ // Move this back to Controller once PHP 5.3 is out (get_called_class())
+ static function select($table, $where = 1, $what = "*", $order_by = "")
+ {
+ $out = array();
+
+ $query = "?";
+ $values = array(1);
+
+ if (is_array($where)) {
+ $values = array();
+ foreach ($where as $k => $v) {
+ $keys[] = "`$k` = :{$k}";
+ $values[":{$k}"] = $v;
+ }
+ $query = implode(" AND ", $keys);
+ }
+
+ if (!empty($order_by)) {
+ list($ob_col, $ob_dir) = explode(" ", $order_by);
+ $order_by = "ORDER BY `$ob_col` $ob_dir";
+ }
+
+ $sql = "SELECT $what FROM `$table` WHERE $query $order_by";
+
+ self::log($sql);
+ self::log($values);
+
+ $stmt = self::db()->prepare($sql);
+ $stmt->execute($values);
+
+ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
+ $out[] = $row;
+ }
+
+ if (count($out) == 1 && $what != "*") {
+ return $out[0][$what];
+ }
+
+ return $out;
+
+ }
+
+ public function js($string)
+ {
+ return '<script type="text/javascript" charset="utf-8">
+ ' . $string . '
+ </script>';
+ }
+
+}
+
+ function __autoload($class_name)
+ {
+ $class_name = ucfirst($class_name);
+ include_once "{$class_name}.php";
+ if (!class_exists($class_name)) {
+ exit(App::error("{$class_name}: no such class"));
+ }
+ }
+
+?> \ No newline at end of file
diff --git a/Controller.php b/Controller.php
new file mode 100644
index 0000000..568f192
--- /dev/null
+++ b/Controller.php
@@ -0,0 +1,368 @@
+<?php
+
+/**
+ * main/Controller.php
+ * "VC" version of Beof
+ * @TODO: transfer boeuf.php here
+ */
+
+class Controller
+{
+ public $view; // Where to store the data to be rendered
+ public $pageTitle = ""; // Title of the rendered page
+ public $defaultAction = ""; // Method to run when none specified
+
+ const OrderBy = "";
+ const HasMany = "";
+ const ManyToMany = "";
+ const ForeignKey = "";
+ const TableName = "";
+ const TableSelect = "";
+
+ function __construct()
+ {
+ // Child classes should always have the same name as their tables
+ $this->table = strtolower(get_class($this));
+ $this->result = null;
+ return $this;
+ }
+
+ public function get($id)
+ {
+ return $this->find(array('id' => $id));
+ }
+
+ /**
+ * The New Find
+ */
+ public function find($where = null, $sort = false, $limit = false)
+ {
+ $select = "*"; $q = "";
+
+ // Allow custom queries
+ if (is_array($where))
+ {
+ foreach ($where as $k => $v)
+ {
+ $values[] = $v;
+ $args = explode(" ", $k);
+ $col = array_shift($args);
+ $op = implode(" ", $args);
+
+ // No column name given, default to "id"
+ if (empty($col)) {
+ $col = "id";
+ }
+
+ // No operator given, default to "="
+ if (empty($op)) {
+ $op = "=";
+ }
+
+ $tmp = (empty($q)) ? ' WHERE ' : ' AND ';
+ $q .= $tmp . $col . ' ' . $op . ' ?';
+
+ }
+ } else {
+ $q = "WHERE `{$this->table}`.`id` = ?";
+ $values = array($where);
+ }
+
+ if ($where == null) {
+ $q = "WHERE ?";
+ $values = array(1);
+ }
+
+// $schema = App::conf('tables');
+// $this->schema = $schema[$this->table];
+
+ // Ugly hack until PHP 5.3
+ $i_sort = eval("return {$this->table}::OrderBy;");
+ $i_fk = eval("return {$this->table}::ForeignKey;");
+ $i_mtm = eval("return {$this->table}::ManyToMany;");
+ $i_select = eval("return {$this->table}::TableSelect;");
+
+// $orderBy = ($sort) ? $sort :
+
+ if ($sort) {
+ list($col, $dir) = explode(' ', $sort);
+ $sort = "ORDER BY `{$this->table}`.`$col` $dir";
+ }
+
+ if (!$sort && $i_sort) {
+ $sort = "ORDER BY `{$this->table}`.{$i_sort}";
+ }
+
+ if ($i_select) {
+ list($select_col, $args) = explode(",", $i_select);
+ $select .= ", $args AS `{$select_col}`";
+ }
+
+ $sql = "SELECT $select FROM `{$this->table}` $q $sort";
+
+ if ($limit) {
+ $sql .= " LIMIT $limit";
+ }
+
+ $stmt = DB::query($sql, $values);
+
+ $i = 0;
+
+ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row)
+ {
+ $this->data[$i] = $row;
+ $this->find_parent($row, $i);
+ $this->find_children($row, $i);
+ $i++;
+ }
+
+ return $this;
+
+ }
+
+ /**
+ * Return all child rows for this row
+ */
+ private function find_children($row, $i)
+ {
+ $id = $row['id']; // ID of the parent
+ $fk = explode(",", eval("return $this->table::HasMany;"));
+
+ if (empty($fk[0])) {
+ return false;
+ }
+
+ foreach ($fk as $child)
+ {
+ $sql = "SELECT * FROM `$child` WHERE `{$this->table}_id` = ?";
+
+ $ref_schema = App::conf('tables');
+ $ref_schema = $ref_schema[$child];
+
+ if (@in_array($this->table, $ref_schema['belongsToMany'])) // m/n
+ {
+ $sql = "SELECT `{$child}`.*, `{$child}_{$this->table}`.*,
+ `{$child}_{$this->table}`.id AS {$child}_{$this->table}_id,
+ `{$child}`.*
+ FROM `{$child}_{$this->table}`, `{$this->table}`, `$child`
+ WHERE `{$child}_{$this->table}`.`{$this->table}_id` = `$this->table`.id AND
+ `{$child}_{$this->table}`.`{$child}_id` = `$child`.id AND
+ `{$this->table}`.id = ?
+ ORDER BY `{$child}`.{$ref_schema['orderBy']}";
+ } else if (@in_array ($table, $ref_schema['belongsTo'])) { // 1/m
+ $sql = "SELECT * FROM `$ref` WHERE `$ref`.`{$table}_id` = ?";
+ }
+
+ $stmt = App::db()->prepare($sql);
+ $stmt->execute(array($id));
+ $this->data[$i][$child] = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ }
+ }
+
+ /**
+ * Find all rows for this row
+ */
+ private function find_parent($row, $i)
+ {
+ $select = "*";
+ $fk = explode(",", eval("return {$this->table}::ForeignKey;"));
+
+ // No parents defined
+ if (empty($fk[0])) {
+ return false;
+ }
+
+ foreach ($fk as $parent)
+ {
+ $fkey = "id";
+ $lkey = "{$parent}_id";
+ /*
+ if ($this->schema['foreignKey'][$parent])
+ {
+ list($lkey, $fkey) = explode("|", $this->schema['foreignKey'][$parent]);
+ }
+ */
+ $parent_id = $row[$lkey];
+
+// $ref_schema = App::conf('tables');
+// $ref_schema = $ref_schema[$parent];
+ $ref_schema = $fk[''];
+
+ if ($ref_schema['select'])
+ {
+ foreach ($ref_schema['select'] as $a => $b)
+ {
+ $select .= ", $b AS `{$a}`";
+ }
+ }
+
+ $sql = "SELECT $select FROM `{$parent}` WHERE `{$fkey}` = ?";
+
+ $stmt = App::db()->prepare($sql);
+ $stmt->execute(array($parent_id));
+ $this->data[$i][$parent] = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ }
+
+ }
+
+ private function find_parents()
+ {
+
+ }
+
+ /**
+ * Insert this thing in the DB and return inserted
+ * thing
+ */
+ public function insert($data = null)
+ {
+ if (!$data) {
+ $data = $_POST;
+ }
+
+ if (empty($data)) {
+ App::log("Attempted to insert empty data");
+ exit(App::error("Insert failed - nothing to insert"));
+ }
+
+ $insert = "";
+ $values = array();
+
+ foreach($data as $k => $v) {
+ $insert .= "`{$k}`, ";
+ $values[":{$k}"] = $v;
+ }
+
+ $insert = rtrim($insert, ", ");
+ $val = implode(", ", array_keys($values));
+ $sql = "INSERT INTO `{$this->table}` ({$insert}) VALUES ({$val})";
+
+ App::log($sql);
+
+ return DB::query($sql, $values);
+
+ }
+
+ /**
+ * Delete this thing
+ */
+ public function delete()
+ {
+ if (empty($_POST)) {
+ exit(App::error("Delete without arguments"));
+ }
+
+ list($key, $value) = each($_POST);
+
+ $sql = "DELETE FROM `{$this->table}` WHERE `{$key}` = ?";
+
+ return DB::query($sql, $value);
+
+ }
+
+ /**
+ * Update this thing
+ * We keep this in the Controller since it might know
+ * more about the topmost class
+ */
+ public function update($data = null, $where = null)
+ {
+ if (!$data) {
+ $data = $_POST;
+ }
+
+ if (empty($data)) {
+ exit(self::error("Update with empty parameters"));
+ }
+
+ if (empty($where)) {
+ $where = array('id' => 'id');
+ }
+
+ $query = ""; $values = array();
+
+ foreach ($data as $k => $v) {
+ $query .= "`$k` = :$k, ";
+ $values[":{$k}"] = $v;
+ }
+
+ $query = rtrim($query, ", ");
+ list($col, $val) = each($where);
+
+ $sql = "UPDATE `{$this->table}` SET $query WHERE `$col` = :$col";
+
+ return DB::query($sql, $values);
+
+ }
+
+ /**
+ * Render a view
+ */
+ public function render($data = null, $view = null)
+ {
+ // Default to the same view as the method
+ if (!$view) {
+ $bt = debug_backtrace();
+ $view = $bt[1]['function'];
+ }
+
+ if (!$view) {
+ $view = $this->defaultAction;
+ }
+
+ if (!$data) {
+ $data = $this->view;
+ }
+
+ $type = App::type();
+ $template = "../system/views/default.{$type}";
+ $file = "../system/views/{$this->table}/{$view}.{$type}";
+
+ if (!is_file($file)) {
+ exit(App::error("{$this->table}_{$view}_{$type}: no such view"));
+ }
+
+ if ($data) {
+ foreach ($data as $k => $v) {
+ $$k = $v;
+ }
+ }
+
+ // Capture view
+ ob_start();
+ include $file;
+ $view_contents = ob_get_contents();
+ ob_end_clean();
+
+ // Capture template
+ ob_start();
+ include $template;
+ $tpl_contents = ob_get_contents();
+ ob_end_clean();
+
+ $tpl_contents = preg_replace(
+ '/<title>.*?<\/title>/', "<title>{$this->pageTitle}</title>", $tpl_contents
+ );
+
+ echo str_replace('%%page_content%%', $view_contents, $tpl_contents);
+
+ }
+
+ public function match($cols, $match)
+ {
+ foreach ($cols as $col) {
+ $sql .= "`{$col}`,";
+ }
+
+ $sql = rtrim($sql, ",");
+ $sql = "SELECT * FROM `{$this->table}` WHERE MATCH($sql) AGAINST('{$match}')";
+
+ return App::db()->query($sql);
+
+ }
+
+}
+
+?>
diff --git a/Db.php b/Db.php
new file mode 100644
index 0000000..2b7b532
--- /dev/null
+++ b/Db.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * boeuf/Db.php
+ * @author Filipp Lepalaan <filipp@mekanisti.fi>
+ * http://www.php.net/manual/en/language.oop5.patterns.php
+ */
+
+class Db
+{
+ private static $instance = NULL;
+
+ private function __construct()
+ {
+
+ }
+
+ /**
+ * Open persistent connection to database
+ */
+ public static function getInstance()
+ {
+ $c = App::conf();
+
+ if (!self::$instance)
+ {
+ try {
+ self::$instance = new PDO(
+ "{$c['db.driver']}:host={$c['db.host']};dbname={$c['db.name']}",
+ $c['db.username'], $c['db.password'], array(PDO::ATTR_PERSISTENT => true)
+ );
+
+ self::$instance->query("SET NAMES utf8");
+
+ } catch (PDOException $e) {
+ exit(App::error($e->getMessage()));
+ }
+
+ }
+
+ return self::$instance;
+
+ }
+
+ /**
+ * Deny cloning
+ */
+ public function __clone()
+ {
+ trigger_error("Hello, ich name was Singleton. Cloning is not allowed", E_USER_ERROR);
+ }
+
+ /**
+ * Execute an SQL query
+ */
+ public function query($sql, $data = null)
+ {
+ if (!$data) {
+ $data = array();
+ }
+
+ // Might be just a string
+ if (!is_array($data)) {
+ $data = array($data);
+ }
+
+ try {
+
+ $stmt = self::getInstance()->prepare($sql);
+ $result = $stmt->execute($data);
+
+ if (!$result) {
+ $e = $stmt->errorInfo();
+ exit(App::error($e[2]));
+ }
+
+ } catch (PDOException $e) {
+ $error = $e->getMessage() . $sql;
+ App::log($error);
+ exit(App::error($error));
+ }
+
+ // Select statements need the query results
+ if (preg_match('/^select/i', $sql)) {
+ return $stmt;
+ }
+
+ if (empty($data['id'])) {
+ $data['id'] = self::getInstance()->lastInsertId();
+ }
+
+ return $data;
+
+ }
+
+}
+
+?> \ No newline at end of file