diff options
-rw-r--r-- | .DS_Store | bin | 0 -> 6148 bytes | |||
-rw-r--r-- | App.php | 230 | ||||
-rw-r--r-- | Controller.php | 368 | ||||
-rw-r--r-- | Db.php | 98 |
4 files changed, 696 insertions, 0 deletions
diff --git a/.DS_Store b/.DS_Store Binary files differnew file mode 100644 index 0000000..d150107 --- /dev/null +++ b/.DS_Store @@ -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); + + } + +} + +?> @@ -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 |