Location: PHPKode > scripts > orange-bean > orange-bean.php
<?php
# http://code.google.com/p/orange-bean/
# MIT License
# Shrunk with http://code.google.com/p/php-compressor/
namespace{class R{static function __callStatic($C,$S){static$X;if(!$X)$X=new Lexa\OrangeBean\Api;return call_user_func_array(array($X,$C),$S);}}}namespace Lexa\OrangeBean{class Error extends\Exception{}class Bean{private$kind,$props=array();var$id;final function __construct($A){$this->kind=$A;}function __toString(){$J=$this->kind;if($this->id!==null)$J.=" (".$this->id.")";return$J;}final function kind(){return$this->kind;}final function export(){return$this->props;}final function import($H){$this->props=$H;}final function __isset($C){return isset($this->props[$C]);}final function __unset($C){unset($this->props[$C]);}final function __get($C){$B=$this->__isset($C)?$this->props[$C]:null;$e="format_$C";if(method_exists($this,$e))$B=$this->$e($B);return$B;}final function __set($C,$B){$t="parse_$C";if(method_exists($this,$t))$B=$this->$t($B);$this->props[$C]=$B;}function after_dispense(){}function before_load(){}function after_load(){}function before_store(){}function after_store(){}function before_trash(){}function after_trash(){}}class Manager{var$model_formatter,$observers=array(),$backend;function dispense($A){$Z=$this->model_class($A);$E=new$Z($A);if(!($E instanceof Bean))throw new Error("Class '$Z' is not a Bean");$this->signal($E,"after_dispense");return$E;}function store($E){$this->signal($E,"before_store");$E->id=$this->backend->store($E->kind(),$E->id,$E->export());$this->signal($E,"after_store");return$E->id;}function load($A,$G){$H=$this->backend->load($A,$G);if(!is_array($H))return null;return$this->load_core($A,$G,$H);}function load_core($A,$G,$H){$E=$this->dispense($A);$this->signal($E,"before_load");$E->import($H);$E->id=$G;$this->signal($E,"after_load");return$E;}function trash($E){if($E->id===null)return;$this->signal($E,"before_trash");$this->backend->trash($E->kind(),$E->id);$this->signal($E,"after_trash");}function model_class($A){$Z=$this->model_formatter?call_user_func($this->model_formatter,$A):"Model_$A";if(class_exists($Z))return$Z;return"Lexa\\OrangeBean\\Bean";}protected function signal($E,$i){$E->$i();foreach($this->observers as$z){if(!method_exists($z,$i))continue;$z->$i($E);}}}abstract class PDOBackend{private$dsn,$login,$pw,$_pdo,$_schema,$frozen=false,$tx_level=0,$sql_logger,$cache=array(),$cache_size=50;function __construct($O,$U=null,$W=null){$this->dsn=$O;$this->login=$U;$this->pw=$W;}function freeze(){$this->frozen=true;}function sql_logger($CB){$this->sql_logger=$CB;}protected abstract function type_rank_from_sql_type($B);protected abstract function type_rank_from_value($B);protected abstract function sql_type_from_type_rank($f);protected abstract function load_schema();function schema(){if(!$this->_schema)$this->_schema=$this->load_schema();return$this->_schema;}protected function invalidate_schema(){$this->_schema=null;}function rows($D,$F=null){return$this->cached_fetch($D,$F,0,1);}function row($D,$F=null){return$this->cached_fetch($D,$F,0,0);}function col($D,$F=null){return$this->cached_fetch($D,$F,1,1);}function cell($D,$F=null){return$this->cached_fetch($D,$F,1,0);}function rows_iterator($D,$F=null){return$this->exec_core($D,$F);}function col_iterator($D,$F=null){$N=$this->exec_core($D,$F);$N->setFetchMode(\PDO::FETCH_COLUMN,0);return$N;}function exec($D,$F=null){return$this->exec_core($D,$F)->rowCount();}protected function cached_fetch($D,$F,$v,$u){$D=trim($D);$Y=json_encode(array($v+$u,$D,$F));$J=$this->cache_size&&isset($this->cache[$Y])?$this->cache[$Y]:$this->{$v?"col_iterator":"rows_iterator"}($D,$F)->{$u?"fetchAll":"fetch"}();if($this->cache_size||isset($this->cache[$Y])){unset($this->cache[$Y]);$this->cache[$Y]=$J;if(count($this->cache)>$this->cache_size)array_splice($this->cache,0,$this->cache_size/2);}return$J;}protected function exec_core($D,$F){if(!preg_match("/^(select|show|pragma)\s/i",$D))$this->cache=array();if($this->sql_logger)$GB=microtime(true);$N=$this->pdo()->prepare($D);$N->execute($F);if($this->sql_logger)call_user_func($this->sql_logger,$D,$F,microtime(true)-$GB);$N->setFetchMode(\PDO::FETCH_ASSOC);return$N;}function insert_id(){return$this->pdo()->lastInsertId();}function pdo(){if(!$this->_pdo)$this->_pdo=new\PDO($this->dsn,$this->login,$this->pw,$this->pdo_options());return$this->_pdo;}protected function pdo_options(){return array(\PDO::ATTR_ERRMODE=>\PDO::ERRMODE_EXCEPTION);}function quote_name($C){return"`$C`";}function quote_value($B){if($B===null)return"null";if(is_numeric($B))return$B;return$this->pdo()->quote($B);}function store($A,$G,$H){if(!$this->frozen)$this->check_schema($A,$H);if($G===null){$this->exec($this->generate_insert_statement($A,$H));return$this->insert_id();}if(count($H)){if(!$this->exec($this->generate_update_statement($A,$G,$H)))throw new Error("Row not found");}return$G;}function load($A,$G){if(!$this->frozen&&!$this->table_exists($A))return null;$I=$this->row("select * from ".$this->quote_name($A)." where id = ".$this->quote_value($G));if($I===false)return null;unset($I["id"]);return$I;}function trash($A,$G){if(!$this->frozen&&!$this->table_exists($A))return;$this->exec("delete from ".$this->quote_name($A)." where id = ".$this->quote_value($G));}protected function generate_insert_statement($A,$H){if(!count($H))$H["id"]=null;$x=array();$s=array();foreach($H as$C=>$B){$x[]=$this->quote_name($C);$s[]=$this->quote_value($B);}return"insert into ".$this->quote_name($A)." (".join(", ",$x).") values (".join(", ",$s).")";}protected function generate_update_statement($A,$G,$H){$D="";foreach($H as$C=>$B){if(strlen($D))$D.=", ";$D.=$this->quote_name($C)." = ".$this->quote_value($B);}return"update ".$this->quote_name($A)." set ".$D." where id = ".$this->quote_value($G);}protected function columns_from_data($H){$J=array();foreach($H as$C=>$B)$J[$C]=$this->type_rank_from_value($B);return$J;}protected function table_exists($A){return array_key_exists($A,$this->schema());}protected function check_schema($A,$H){$w=$this->columns_from_data($H);if(!$this->table_exists($A)){$this->exec($this->generate_create_table_statement($A,$w));$this->invalidate_schema();}else{$N=$this->schema();$M=$N[$A];$L=array();foreach($w as$C=>$P){if(!isset($M[$C])||$P>$M[$C])$L[$C]=$P;}if(count($L)){$this->update_schema($A,$M,$L);$this->invalidate_schema();}}}protected abstract function pk_sql_type();protected function create_table_statement_suffix(){return"";}protected function generate_create_table_statement($A,$R){$D="create table ".$this->quote_name($A)." (id ".$this->pk_sql_type();foreach($R as$C=>$HB)$D.=", ".$this->quote_name($C)." ".$this->sql_type_from_type_rank($HB);return$D.") ".$this->create_table_statement_suffix();}protected abstract function update_schema($A,$M,$L);function transaction($c){$j=$this->pdo();$j->beginTransaction();$this->tx_level++;$q=false;$Q=null;try{$q=$c()===false;}catch(\Exception$Q){}if($q||$Q){$this->cache=array();$j->rollBack();}else{$j->commit();}$this->tx_level--;if($Q)throw$Q;}function in_transaction(){return$this->tx_level>0;}function cache($y=null){if($y===null)return$this->cache_size;$this->cache_size=max(0,(int)$y);}}class SQLiteBackend extends PDOBackend{const NUMERIC=0,TEXT=1;protected function type_rank_from_sql_type($B){$B=strtolower($B);if($B=="numeric")return self::NUMERIC;if($B=="text")return self::TEXT;return PHP_INT_MAX;}protected function type_rank_from_value($B){$B=strval($B);if($B===""||is_numeric($B))return self::NUMERIC;return self::TEXT;}protected function sql_type_from_type_rank($f){switch($f){case self::NUMERIC:return"numeric";case self::TEXT:return"text";}throw new Error;}protected function load_schema(){$J=array();$IB=$this->col("select name from sqlite_master where type = 'table' and name <> 'sqlite_sequence'");foreach($IB as$a){$R=array();foreach($this->rows("pragma table_info(".$this->quote_name($a).")")as$I){if($I["pk"])continue;$R[$I["name"]]=$this->type_rank_from_sql_type($I["type"]);}$J[$a]=$R;}return$J;}protected function pk_sql_type(){return"integer primary key";}protected function update_schema($A,$M,$L){$r=false;foreach($L as$C=>$P){if(isset($M[$C])){$r=true;break;}}if($r){$V="__orange_bean_tmp";$h=$this->quote_name($A);$EB=array("drop table if exists $V",$this->generate_create_table_statement($V,$M),"insert into $V select * from $h","drop table $h",$this->generate_create_table_statement($A,array_merge($M,$L)),"insert into $h select * from $V","drop table $V");foreach($EB as$FB)$this->exec($FB);}else{foreach($L as$C=>$P){$this->exec("alter table ".$this->quote_name($A)." add ".$this->quote_name($C)." ".$this->sql_type_from_type_rank($P));}}}}class MySQLBackend extends PDOBackend{const BOOL=0,INT8=1,INT32=2,DOUBLE=3,TEXT8=4,TEXT24=5;protected function type_rank_from_sql_type($B){if($B=="set('1')")return self::BOOL;if($B=="tinyint(4)")return self::INT8;if($B=="int(11)")return self::INT32;if($B=="double")return self::DOUBLE;if($B=="varchar(255)")return self::TEXT8;if($B=="mediumtext")return self::TEXT24;return PHP_INT_MAX;}protected function type_rank_from_value($B){$d=strval($B);if($d===""||$d==="1")return self::BOOL;if(is_numeric($d)){if($B==intval($B)){if($B>=-128&&$B<=127)return self::INT8;if($B>=-0x80000000&&$B<=0x7fffffff)return self::INT32;}return self::DOUBLE;}if(strlen($d)<256)return self::TEXT8;return self::TEXT24;}protected function sql_type_from_type_rank($f){switch($f){case self::BOOL:return"set('1')";case self::INT8:return"tinyint(4)";case self::INT32:return"int(11)";case self::DOUBLE:return"double";case self::TEXT8:return"varchar(255)";case self::TEXT24:return"mediumtext";}throw new Error;}protected function load_schema(){$J=array();foreach($this->col("show tables")as$a){$R=array();foreach($this->rows("describe ".$this->quote_name($a))as$I){if($I["Key"]=="PRI")continue;$R[$I["Field"]]=$this->type_rank_from_sql_type($I["Type"]);}$J[$a]=$R;};return$J;}protected function pk_sql_type(){return"int unsigned not null auto_increment primary key";}protected function update_schema($A,$M,$L){$D="";foreach($L as$C=>$P){if(strlen($D))$D.=", ";$g=$this->quote_name($C);$o=$this->sql_type_from_type_rank($P);if(isset($M[$C]))$D.="change $g $g $o";else$D.="add $g $o";}$this->exec("alter table ".$this->quote_name($A)." ".$D);}protected function pdo_options(){return array(\PDO::MYSQL_ATTR_INIT_COMMAND=>"set names utf8")+parent::pdo_options();}protected function create_table_statement_suffix(){return"default charset=utf8 collate=utf8_unicode_ci";}}class RowsToBeansIterator extends\IteratorIterator{private$api,$kind;function __construct($T,$X,$A){parent::__construct($T);$this->api=$X;$this->kind=$A;}function current(){return$this->api->row_to_bean($this->kind,parent::current());}}class Api{private$man;function setup($O,$U=null,$W=null){$m=new Manager;$m->backend=$this->create_backend($O,$U,$W);$this->man=$m;}function __call($C,$K){switch($C){case"rows":case"col":case"row":case"cell":case"exec":case"rows_iterator":case"col_iterator":return$this->backend()->$C($K[0],$this->params_from_args($K,1));case"pdo":case"quote_name":case"quote_value":case"sql_logger":case"freeze":case"transaction":case"in_transaction":case"cache":return call_user_func_array(array($this->backend(),$C),$K);case"find":case"find_one":case"find_iterator":return$this->find_impl($K[0],count($K)>1?$K[1]:null,$this->params_from_args($K,2),$C=="find_iterator",$C=="find_one");}if(substr($C,0,9)=="uncached_")return$this->wrap_uncached_call(substr($C,9),$K);throw new Error("No such method: $C");}function model_formatter($e){$this->man->model_formatter=$e;}function model_class($A){return$this->man->model_class($A);}function add_observer($k){$this->man->observers[]=$k;}function remove_observer($k){$n=array();foreach($this->man->observers as$p){if($p!==$k)$n[]=$p;}$this->man->observers=$n;}function dispense($A){return$this->man->dispense($A);}function load($A,$G){return$this->man->load($A,$G);}function store($E){$b=$this->man;$this->implicit_transaction(function()use($b,$E,&$G){$G=$b->store($E);});return$G;}function trash($E){$b=$this->man;$this->implicit_transaction(function()use($b,$E){$b->trash($E);});}function row_to_bean($A,$I){$G=$I["id"];unset($I["id"]);return$this->man->load_core($A,$G,$I);}function rows_to_beans($A,$T){$J=array();foreach($T as$I)$J[]=$this->row_to_bean($A,$I);return$J;}protected function create_backend($O,$U,$W){if(substr($O,0,7)=="sqlite:")return new SQLiteBackend($O);if(substr($O,0,6)=="mysql:")return new MySQLBackend($O,$U,$W);throw new Error("DSN not supported");}protected function backend(){return$this->man->backend;}protected function implicit_transaction($c){if($this->in_transaction())$c();else$this->transaction($c);}protected function find_impl($A,$l,$F,$_,$AB){$D="select * from ".$this->quote_name($A);if($l)$D.=" ".$l;if($_)return new RowsToBeansIterator($this->rows_iterator($D,$F),$this,$A);$T=$this->rows($D,$F);if(!$AB)return$this->rows_to_beans($A,$T);foreach($T as$I)return$this->row_to_bean($A,$I);}protected function params_from_args($S,$DB){array_splice($S,0,$DB);if(count($S)&&is_array($S[0]))return$S[0];return$S;}protected function wrap_uncached_call($C,$K){$BB=$this->cache();$this->cache(false);try{$J=call_user_func_array(array($this,$C),$K);}catch(\Exception$Q){}$this->cache($BB);if(isset($Q))throw$Q;return$J;}}}
Return current item: orange-bean