_valid = false; if ($id !== null) { $this->_load_record($id); $this->_loaded = $this->_valid; } else { $this->_get_fields(); $this->_loaded = false; $this->_valid = true; } } public static function cache_connect($host, $port, $weight = 50) { if (Model::$_cache === null) { Model::$_cache = new Memcached(); } Model::$_cache->addServer($host, $port, $weight); } private function _load_record($id) { $class = get_called_class(); if (property_exists($class, "table")) { $v = get_class_vars($class); $table = $v["table"]; } else { $table = strtolower($class); } $r = DB::getInstance()->select($table, $id); if ($r === false) { $this->_valid = false; return; } foreach ($r as $k=>$v) { $this->_fields[] = $k; $this->_virgin[$k] = $v; } if (property_exists($class, "transform")) { foreach ($this->_fields as $k) { if (array_key_exists($k, $this->transform)) { $v = $this->transform[$k]; $f = "from_" . $v; if (method_exists($this, $f)) { $this->_data[$k] = $this->$f($this->_virgin[$k]); } else { $this->_data[$k] = $this->_virgin[$k]; } } else { $this->_data[$k] = $this->_virgin[$k]; } } } else { foreach ($this->_fields as $k) { $this->_data[$k] = $this->_virgin[$k]; } } $this->_valid = true; } public static function find($where = []) { $class = get_called_class(); if (property_exists($class, "table")) { $v = get_class_vars($class); $table = $v["table"]; } else { $table = strtolower($class); } return new Query($class, $table, $where); } private function _get_fields() { $class = get_called_class(); if ($class == "Model") { // Light model being used for xfer return; } if (property_exists($class, "table")) { $v = get_class_vars($class); $table = $v["table"]; } else { $table = strtolower($class); } $this->_fields = []; $q = DB::getInstance()->query("describe `" . $table . "`"); while ($r = DB::getInstance()->nextRecord($q)) { $this->_fields[] = $r->Field; $this->_data[$r->Field] = null; } $this->_valid = true; } public function from_json($data) { $d = json_decode($data); if ($d) return $d; return new stdClass; } public function to_json($data) { return json_encode($data); } public function valid() { return $this->_valid; } public function loaded() { return $this->_loaded; } public function save() { Model::raw_on(); $class = get_called_class(); if (property_exists($class, "table")) { $v = get_class_vars($class); $table = $v["table"]; } else { $table = strtolower($class); } $_save = []; if (property_exists($class, "transform")) { foreach ($this->_fields as $k) { if (array_key_exists($k, $this->transform)) { $v = $this->transform[$k]; $f = "to_" . $v; if (method_exists($this, $f)) { $_save[$k] = $this->$f(@$this->$k); } else { $v = __unref($this->$k); if (is_string($v)) $v = trim($v); $_save[$k] = $v; } } else { $v = __unref($this->$k); if (is_string($v)) $v = trim($v); $_save[$k] = $v; } } } else { foreach ($this->_fields as $k) { $v = __unref($this->$k); if (is_string($v)) $v = trim($v); $_save[$k] = $v; } } $_update = []; foreach ($_save as $k=>$v) { if (array_key_exists($k, $this->_virgin)) { if ($_save[$k] != $this->_virgin[$k]) { $_update[$k] = $v; $this->_virgin[$k] = $v; } } else { $_update[$k] = $v; $this->_virgin[$k] = $v; } } $triggers = []; if ($this->_loaded) { if (count($_update) > 0) { if (in_array("updated", $this->_fields)) { $_update['updated'] = time(); } DB::getInstance()->update($table, $this->id, $_update); } } else { $_update = $this->_virgin; if (in_array("created", $this->_fields)) { $_update['created'] = time(); } $this->id = DB::getInstance()->insert($table, $_update); $this->_loaded = true; } foreach ($_update as $k=>$v) { if (property_exists($class, "_triggers")) { if (array_key_exists($k, $this->_triggers)) { $triggers[$this->_triggers[$k]] = 1; } } } foreach ($triggers as $trigger=>$count) { if ($trigger instanceof \Closure) { $trigger->call($this); continue; } if (is_array($trigger)) { $c = $trigger[0]; $f = $trigger[1]; $c::$f(); continue; } if (is_string($trigger)) { $this->$trigger(); continue; } } Model::raw_off(); } public static function raw_on() { Model::$_raw = true; } public static function raw_off() { Model::$_raw = false; } static public function raw() { return Model::$_raw; } public function delete() { $class = get_called_class(); if (property_exists($class, "table")) { $v = get_class_vars($class); $table = $v["table"]; } else { $table = strtolower($class); } if (method_exists($this, "on_delete")) { $this->on_delete(); } DB::getInstance()->query("delete from `" . $table . "` where id=:id", ["id" => $this->_data["id"]]); } public function __toString() { return "" . $this->_data["id"]; } public function __toInt() { return (int)$this->_data["id"]; } public function jsonSerialize() : mixed { $out = []; foreach ($this->_fields as $f) { $out[$f] = $this->_data["$f"]; } foreach ($this->_fields as $f) { if (array_key_exists($f, $this->_objects)) { $out[$f] = $this->_objects["$f"]; } } if (property_exists($this, "_computed")) { foreach ($this->_computed as $k=>$v) { $out[$k] = $this->$v(); } } return $out; } private function __get_field_class($f) { if (!property_exists($this, "_classes")) return null; if (!array_key_exists($f, $this->_classes)) return null; return $this->_classes[$f]; } public function __get($k) { if (property_exists($this, "_computed")) { if (array_key_exists($k, $this->_computed)) { $c = $this->cache_get($k); if ($c) return $c; $func = $this->_computed[$k]; $c = $this->$func(); $this->cache_set($k, $c); return $c; } } if (!in_array($k, $this->_fields)) { return null; } $class = $this->__get_field_class($k); if ($class != null) { if (array_key_exists($k, $this->_objects)) return $this->_objects[$k]; } return $this->_data[$k]; } public function __set($key, $val) { if (!in_array($key, $this->_fields)) return false; $class = $this->__get_field_class($key); if ($class != null) { if ($val instanceof Model) { if ($val instanceof $class) { $this->_data[$val] = $val->id; $this->_objects[$key] = $val; return true; } throw new Exception('Class mismatch'); return false; } if (is_numeric($val)) { $this->_data[$key] = $val; $this->_objects[$key] = new $class($val); return true; } $val = (int)$val; $this->_data[$key] = $val; $this->_objects[$key] = new $class($val); return true; } $this->_data[$key] = $val; unset($this->_objects[$key]); return true; } public function __isset($key) { if (!in_array($key, $this->_fields)) return false; return true; } public function __unset($key) { if (!in_array($key, $this->_fields)) return; $this->_data[$key] = null; unset($this->_objects[$key]); } public function load($key = null) { if ($key === null) { foreach ($this->_fields as $key) { $this->load($key); } return true; } if (!in_array($key, $this->_fields)) { return false; } $class = $this->__get_field_class($key); if ($class == null) { return false; } if ($this->_data[$key] === null) { return false; } $ob = new $class($this->_data[$key]); if (!$ob->valid()) return false; $this->_objects[$key] = $ob; return true; } private function cache_key($key) { $class = get_called_class(); return sprintf("%s[%d]::%s", $class, $this->id, $key); } public function cache_set($key, $val) { $key = $this->cache_key($key); Model::$_cache->set($key, $val, $this->_timeout); } public function cache_get($key) { $key = $this->cache_key($key); return Model::$_cache->get($key); } public function cache_invalidate($key) { $key = $this->cache_key($key); Model::$_cache->delete($key); } public static function get_all_models() { $res = []; foreach (get_declared_classes() as $class) { if (is_subclass_of($class, "Model")) { $res[] = $class; } } } public function create_or_update_table() { } public function get_table_name() { $class = get_called_class(); if (property_exists($class, "table")) { $v = get_class_vars($class); return($v["table"]); } return (strtolower($class)); } }