Skip to content
Browse files

splitted files and added sieves

  • Loading branch information...
1 parent 2d80e8c commit 3a9f06af2dcead75fa23b477b502469b251f7bcb gabor de mooij committed Jul 23, 2009
View
11 RedBean/Bean.php
@@ -0,0 +1,11 @@
+<?php
+
+
+
+/**
+ * Object Oriented Database Bean class
+ * Empty Type definition for bean processing
+ *
+ */
+class OODBBean {
+}
View
226 RedBean/Can.php
@@ -0,0 +1,226 @@
+<?php
+
+/**
+ * RedBean Can
+ * @desc a Can contains beans and acts as n iterator, it also enables you to work with
+ * large collections while remaining light-weight
+ * @author gabordemooij
+ */
+class RedBean_Can implements Iterator , ArrayAccess , SeekableIterator , Countable {
+
+ /**
+ *
+ * @var array
+ */
+ private $collectionIDs = null;
+
+ /**
+ *
+ * @var string
+ */
+ private $type = null;
+
+ /**
+ *
+ * @var int
+ */
+ private $pointer = 0;
+
+ /**
+ *
+ * @var int
+ */
+ private $num = 0;
+
+ /**
+ * Constructor
+ * @param $type
+ * @param $collection
+ * @return RedBean_Can $instance
+ */
+ public function __construct( $type="", $collection = array() ) {
+
+ $this->collectionIDs = $collection;
+ $this->type = $type;
+ $this->num = count( $this->collectionIDs );
+ }
+
+ /**
+ * Wraps an OODBBean in a RedBean_Decorator
+ * @param OODBBean $bean
+ * @return RedBean_Decorator $deco
+ */
+ public function wrap( $bean ) {
+
+ $dclass = PRFX.$this->type.SFFX;
+ $deco = new $dclass( floatval( $bean->id ) );
+ $deco->setData( $bean );
+ return $deco;
+
+ }
+
+ /**
+ * Returns the number of beans in this can
+ * @return int $num
+ */
+ public function count() {
+
+ return $this->num;
+
+ }
+
+ /**
+ * Returns all the beans inside this can
+ * @return array $beans
+ */
+ public function getBeans() {
+
+ $rows = RedBean_OODB::fastloader( $this->type, $this->collectionIDs );
+
+ $beans = array();
+
+ if (is_array($rows)) {
+ foreach( $rows as $row ) {
+ //Use the fastloader for optimal performance (takes row as data)
+ $beans[] = $this->wrap( RedBean_OODB::getById( $this->type, $id , $row) );
+ }
+ }
+
+ return $beans;
+ }
+
+ public function slice( $begin=0, $end=0 ) {
+ $this->collectionIDs = array_slice( $this->collectionIDs, $begin, $end);
+ $this->num = count( $this->collectionIDs );
+ }
+
+ /**
+ * Returns the current bean
+ * @return RedBean_Decorator $bean
+ */
+ public function current() {
+ if (isset($this->collectionIDs[$this->pointer])) {
+ $id = $this->collectionIDs[$this->pointer];
+ return $this->wrap( RedBean_OODB::getById( $this->type, $id ) );
+ }
+ else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the key of the current bean
+ * @return int $key
+ */
+ public function key() {
+ return $this->pointer;
+ }
+
+
+ /**
+ * Advances the internal pointer to the next bean in the can
+ * @return int $pointer
+ */
+ public function next() {
+ return ++$this->pointer;
+ }
+
+ /**
+ * Sets the internal pointer to the previous bean in the can
+ * @return int $pointer
+ */
+ public function prev() {
+ if ($this->pointer > 0) {
+ return ++$this->pointer;
+ }else {
+ return 0;
+ }
+ }
+
+ /**
+ * Resets the internal pointer to the first bean in the can
+ * @return void
+ */
+ public function rewind() {
+ $this->pointer=0;
+ return 0;
+ }
+
+ /**
+ * Sets the internal pointer to the specified key in the can
+ * @param int $seek
+ * @return void
+ */
+ public function seek( $seek ) {
+ $this->pointer = (int) $seek;
+ }
+
+ /**
+ * Checks there are any more beans in this can
+ * @return boolean $morebeans
+ */
+ public function valid() {
+ return ($this->num > ($this->pointer+1));
+ }
+
+ /**
+ * Checks there are any more beans in this can
+ * Same as valid() but this method has a more descriptive name
+ * @return boolean $morebeans
+ */
+ public function hasMoreBeans() {
+ return $this->valid();
+ }
+
+ /**
+ * Makes it possible to use this object as an array
+ * Sets the offset
+ * @param $offset
+ * @param $value
+ * @return void
+ */
+ public function offsetSet($offset, $value) {
+ $this->collectionIDs[$offset] = $value;
+ }
+
+ /**
+ * Makes it possible to use this object as an array
+ * Checks the offset
+ * @param $offset
+ * @return boolean $isset
+ */
+ public function offsetExists($offset) {
+ return isset($this->collectionIDs[$offset]);
+ }
+
+ /**
+ * Makes it possible to use this object as an array
+ * Unsets the value at offset
+ * @param $offset
+ * @return void
+ */
+ public function offsetUnset($offset) {
+ unset($this->collectionIDs[$offset]);
+ }
+
+ /**
+ * Makes it possible to use this object as an array
+ * Gets the bean at a given offset
+ * @param $offset
+ * @return RedBean_Decorator $bean
+ */
+ public function offsetGet($offset) {
+
+ if (isset($this->collectionIDs[$offset])) {
+ $id = $this->collectionIDs[$offset];
+ return $this->wrap( RedBean_OODB::getById( $this->type, $id ) );
+ }
+ else {
+ return null;
+ }
+
+ }
+
+
+}
+
View
120 RedBean/DBAdapter.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * Adapter for ADODB database layer AND RedBean
+ * @author gabordemooij
+ *
+ */
+class RedBean_DBAdapter {
+
+ /**
+ *
+ * @var ADODB
+ */
+ private $db = null;
+
+ public static $log = array();
+
+ /**
+ *
+ * @param $database
+ * @return unknown_type
+ */
+ public function __construct($database) {
+ $this->db = $database;
+ }
+
+ /**
+ * Escapes a string for use in a Query
+ * @param $sqlvalue
+ * @return unknown_type
+ */
+ public function escape( $sqlvalue ) {
+ return $this->db->Escape($sqlvalue);
+ }
+
+ /**
+ * Executes SQL code
+ * @param $sql
+ * @return unknown_type
+ */
+ public function exec( $sql ) {
+ self::$log[] = $sql;
+ return $this->db->Execute( $sql );
+ }
+
+ /**
+ * Multi array SQL fetch
+ * @param $sql
+ * @return unknown_type
+ */
+ public function get( $sql ) {
+ self::$log[] = $sql;
+ return $this->db->GetAll( $sql );
+ }
+
+ /**
+ * SQL row fetch
+ * @param $sql
+ * @return unknown_type
+ */
+ public function getRow( $sql ) {
+ self::$log[] = $sql;
+ return $this->db->GetRow( $sql );
+ }
+
+ /**
+ * SQL column fetch
+ * @param $sql
+ * @return unknown_type
+ */
+ public function getCol( $sql ) {
+ self::$log[] = $sql;
+ return $this->db->GetCol( $sql );
+ }
+
+ /**
+ * Retrieves a single cell
+ * @param $sql
+ * @return unknown_type
+ */
+ public function getCell( $sql ) {
+ self::$log[] = $sql;
+ $arr = $this->db->GetCol( $sql );
+ if ($arr && is_array($arr)) return ($arr[0]); else return false;
+ }
+
+ /**
+ * Returns last inserted id
+ * @return unknown_type
+ */
+ public function getInsertID() {
+ // self::$log[] = $sql;
+ return $this->db->getInsertID();
+ }
+
+ /**
+ * Returns number of affected rows
+ * @return unknown_type
+ */
+ public function getAffectedRows() {
+ // self::$log[] = $sql;
+ return $this->db->Affected_Rows();
+ }
+
+ /**
+ * Unwrap the original database object
+ * @return $database
+ */
+ public function getDatabase() {
+ return $this->db;
+ }
+
+ /**
+ * Return latest error message
+ * @return string $message
+ */
+ public function getErrorMsg() {
+ return $this->db->Errormsg();
+ }
+
+}
View
553 RedBean/Decorator.php
@@ -0,0 +1,553 @@
+<?php
+
+
+/**
+ * RedBean decorator class
+ * @desc this class provides additional ORM functionality and defauly accessors
+ * @author gabordemooij
+ */
+class RedBean_Decorator {
+
+ /**
+ *
+ * @var OODBBean
+ */
+ protected $data = null;
+
+ /**
+ *
+ * @var string
+ */
+ protected $type = "";
+
+ /**
+ * @var array
+ */
+
+ protected $problems = array();
+
+
+
+ /**
+ * Constructor, loads directly from main table
+ * @param $type
+ * @param $id
+ * @return unknown_type
+ */
+ public function __construct( $type=false, $id=0, $lock=false ) {
+
+ $id = floatval( $id );
+ if (!$type) {
+ throw new Exception("Undefined bean type");
+ }
+ else {
+ $this->type = preg_replace( "[\W_]","", strtolower($type));
+ //echo $this->type;
+ if ($id > 0) { //if the id is higher than 0 load data
+ $this->data = RedBean_OODB::getById( $this->type, $id, $lock );
+ }
+ else { //otherwise, dispense a regular empty OODBBean
+ $this->data = RedBean_OODB::dispense( $this->type );
+ }
+ }
+ }
+
+ /**
+ * Free memory of a class, drop column in db
+ * @param $property
+ * @return unknown_type
+ */
+ public function free( $property ) {
+ RedBean_OODB::dropColumn( $this->type, $property );
+ }
+
+ /**
+
+ * Quick service to copy post values to properties
+ * @param $selection
+ * @return unknown_type
+ */
+ public function importFromPost( $selection=null ) {
+
+ if (!$selection) {
+ $selection = array_keys($_POST);
+ }
+
+ if (is_string($selection)) {
+ $selection = explode(",",$selection);
+ }
+
+ if ($selection && is_array($selection) && count($selection) > 0) {
+ foreach( $selection as $field ) {
+ $setter = "set".ucfirst( $field );
+ if (isset( $_POST[$field] )) {
+ $resp = $this->$setter( $_POST[ $field ] );
+ if ($resp !== true) {
+ $this->problems[$field] = $resp;
+ }
+ }
+
+ }
+
+ if (count($this->problems)===0) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+
+ }
+
+ /**
+ * Imports an array or object
+ * If this function returns boolean true, no problems
+ * have occurred during the import and all values have been copies
+ * succesfully.
+ * @param $arr or $obj
+ * @return boolean $anyproblems
+ */
+ public function import( $arr ) {
+
+ foreach( $arr as $key=>$val ) {
+ $setter = "set".ucfirst( $key );
+ $resp = $this->$setter( $val );
+ if ($resp !== true) {
+ $this->problems[$key] = $resp;
+ }
+ }
+
+
+ if (count($this->problems)===0) {
+ return true;
+ }
+ else {
+ return false;
+ }
+
+ }
+
+ /**
+ * Returns a list filled with possible problems
+ * that occurred while populating the model
+ * @return unknown_type
+ */
+ public function problems() {
+ return $this->problems;
+ }
+
+ /**
+ * Magic method call, this provides basic accessor functionalities
+ */
+ public function __call( $method, $arguments ) {
+ return $this->command( $method, $arguments );
+ }
+
+ /**
+ * Magic getter. Another way to handle accessors
+ */
+ public function __get( $name ) {
+ $name = strtolower( $name );
+ return isset($this->data->$name) ? $this->data->$name : null;
+ }
+
+ /**
+ * Magic setter. Another way to handle accessors
+ */
+ public function __set( $name, $value ) {
+ $name = strtolower( $name );
+ $this->data->$name = $value;
+ }
+
+ /**
+ * To perform a command normally handles by the magic method __call
+ * use this one. This makes it easy to overwrite a method like set
+ * and then route its result to the original method
+ * @param $method
+ * @param $arguments
+ * @return unknown_type
+ */
+ public function command( $method, $arguments ) {
+ if (strpos( $method,"set" ) === 0) {
+ $prop = substr( $method, 3 );
+ $this->$prop = $arguments[0];
+ return $this;
+
+ }
+ elseif (strpos($method,"getRelated")===0) {
+ $prop = strtolower( substr( $method, 10 ) );
+ $beans = RedBean_OODB::getAssoc( $this->data, $prop );
+ $decos = array();
+ $dclass = PRFX.$prop.SFFX;
+
+ if ($beans && is_array($beans)) {
+ foreach($beans as $b) {
+ $d = new $dclass();
+ $d->setData( $b );
+ $decos[] = $d;
+ }
+ }
+ return $decos;
+ }
+ elseif (strpos( $method, "get" ) === 0) {
+ $prop = substr( $method, 3 );
+ return $this->$prop;
+ }
+ elseif (strpos( $method, "is" ) === 0) {
+ $prop = strtolower( substr( $method, 2 ) );
+ return ($this->data->$prop ? TRUE : FALSE);
+ }
+ else if (strpos($method,"add") === 0) { //@add
+ $deco = $arguments[0];
+ $bean = $deco->getData();
+ RedBean_OODB::associate($this->data, $bean);
+ return $this;
+ }
+ else if (strpos($method,"remove")===0) {
+ $deco = $arguments[0];
+ $bean = $deco->getData();
+ RedBean_OODB::unassociate($this->data, $bean);
+ return $this;
+ }
+ else if (strpos($method,"attach")===0) {
+ $deco = $arguments[0];
+ $bean = $deco->getData();
+ RedBean_OODB::addChild($this->data, $bean);
+ return $this;
+ }
+ else if (strpos($method,"clearRelated")===0) {
+ $type = strtolower( substr( $method, 12 ) );
+ RedBean_OODB::deleteAllAssocType($type, $this->data);
+ return $this;
+ }
+ else if (strpos($method,"numof")===0) {
+ $type = strtolower( substr( $method, 5 ) );
+ return RedBean_OODB::numOfRelated($type, $this->data);
+
+ }
+ }
+
+ /**
+ * Enforces an n-to-1 relationship
+ * @param $deco
+ * @return unknown_type
+ */
+ public function belongsTo( $deco ) {
+ RedBean_OODB::deleteAllAssocType($deco->getType(), $this->data);
+ RedBean_OODB::associate($this->data, $deco->getData());
+ }
+
+ /**
+ * Enforces an 1-to-n relationship
+ * @param $deco
+ * @return unknown_type
+ */
+ public function exclusiveAdd( $deco ) {
+ RedBean_OODB::deleteAllAssocType($this->type,$deco->getData());
+ RedBean_OODB::associate($deco->getData(), $this->data);
+ }
+
+ /**
+ * Returns the parent object of the current object if any
+ * @return RedBean_Decorator $oBean
+ */
+ public function parent() {
+ $beans = RedBean_OODB::getParent( $this->data );
+ if (count($beans) > 0 ) $bean = array_pop($beans); else return null;
+ $dclass = PRFX.$this->type.SFFX;
+ $deco = new $dclass();
+ $deco->setData( $bean );
+ return $deco;
+ }
+
+ /**
+ * Returns array of siblings (objects with the same parent except the object itself)
+ * @return array $aObjects
+ */
+ public function siblings() {
+ $beans = RedBean_OODB::getParent( $this->data );
+ if (count($beans) > 0 ) {
+ $bean = array_pop($beans);
+ }
+ else {
+ return null;
+ }
+ $beans = RedBean_OODB::getChildren( $bean );
+ $decos = array();
+ $dclass = PRFX.$this->type.SFFX;
+ if ($beans && is_array($beans)) {
+ foreach($beans as $b) {
+ if ($b->id != $this->data->id) {
+ $d = new $dclass();
+ $d->setData( $b );
+ $decos[] = $d;
+ }
+ }
+ }
+ return $decos;
+ }
+
+ /**
+ * Returns array of child objects
+ * @return array $aObjects
+ */
+ public function children() {
+ $beans = RedBean_OODB::getChildren( $this->data );
+ $decos = array();
+ $dclass = PRFX.$this->type.SFFX;
+ if ($beans && is_array($beans)) {
+ foreach($beans as $b) {
+ $d = new $dclass();
+ $d->setData( $b );
+ $decos[] = $d;
+ }
+ }
+ return $decos;
+ }
+
+ /**
+ * Returns whether a node has a certain parent in its ancestry
+ * @param $deco
+ * @return boolean $found
+ */
+ public function hasParent( $deco ) {
+ $me = $this;
+ while( $parent = $me->parent() ) {
+ if ($deco->getID() == $parent->getID()) {
+ return true;
+ }
+ else {
+ $me = $parent;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Searches children of a specific tree node for the target child
+ * @param $deco
+ * @return boolean $found
+ */
+ public function hasChild( $deco ) {
+
+ $nodes = array($this);
+ while($node = array_shift($nodes)) {
+ //echo "<br>checking ".$node->getID();
+ //echo "<br>equals?... ".$deco->getID();
+ if ($node->getID() == $deco->getID() &&
+ ($node->getID() != $this->getID())) {
+ return true;
+ }
+ //echo "<br> no.. get children.. ";
+ if ($children = $node->children()) {
+ $nodes = array_merge($nodes, $children);
+ //echo "<br>new array: ".count($nodes);
+ }
+ }
+ return false;
+
+ }
+
+ /**
+ * Searches if a node has the specified sibling
+ * @param $deco
+ * @return boolean $found
+ */
+ public function hasSibling( $deco ) {
+ $siblings = $this->siblings();
+ foreach( $siblings as $sibling ) {
+ if ($sibling->getID() == $deco->getID()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This function simply copies the model and returns it
+ * @return RedBean_Decorator $oRD
+ */
+ public function copy() {
+ $clone = new self( $this->type, 0 );
+ $clone->setData( $this->getData() );
+ return $clone;
+ }
+
+ /**
+ * Clears all associations
+ * @return unknown_type
+ */
+ public function clearAllRelations() {
+ RedBean_OODB::deleteAllAssoc( $this->getData() );
+ }
+
+ /**
+ * Gets data directly
+ * @return OODBBean
+ */
+ public function getData() {
+ return $this->data;
+ }
+
+ /**
+ * Sets data directly
+ * @param $data
+ * @return void
+ */
+ public function setData( $data ) {
+ $this->data = $data;
+ }
+
+ /**
+ * Inserts or updates the bean
+ * Returns the ID
+ * @return unknown_type
+ */
+ public function save() {
+ return RedBean_OODB::set( $this->data );
+ }
+
+ /**
+ * Deletes the bean
+ * @param $deco
+ * @return unknown_type
+ */
+ public static function delete( $deco ) {
+ RedBean_OODB::trash( $deco->getData() );
+ }
+
+
+ /**
+ * Explicitly forward-locks a decorated bean
+ * @return unknown_type
+ */
+ public function lock() {
+ RedBean_OODB::openBean($this->getData());
+ }
+
+ /**
+ * Explicitly unlocks a decorated bean
+ * @return unknown_type
+ */
+ public function unlock() {
+ RedBean_OODB::closeBean( $this->getData());
+ }
+
+
+
+
+ /**
+ * Closes and unlocks the bean
+ * @param $deco
+ * @return unknown_type
+ */
+ public static function close( $deco ) {
+ RedBean_OODB::closeBean( $deco->getData() );
+ }
+
+ /**
+ * Creates a redbean decorator for a specified type
+ * @param $type
+ * @param $id
+ * @return unknown_type
+ */
+ public static function make( $type="", $id ){
+ return new RedBean_Decorator( $type, $id );
+ }
+
+
+ /**
+ * Exports a bean to a view
+ * @param $bean
+ * @return unknown_type
+ */
+ public function exportTo( &$bean, $overridebean=false ) {
+
+
+ foreach($this->data as $prop=>$value) {
+
+ //what value should we use?
+ if (is_object($overridebean) && isset($overridebean->$prop)) {
+ $value = $overridebean->$prop;
+ }
+ elseif (is_array($overridebean) && isset($overridebean[$prop])) {
+ $value = $overridebean[$prop];
+ }
+
+ if (is_object($value)){
+ $value = $value->getID();
+ }
+
+ if (is_object($bean)) {
+ $bean->$prop = $value;
+ }
+ elseif (is_array($bean)) {
+ $bean[$prop] = $value;
+ }
+ }
+
+ return $bean;
+ }
+
+
+ /**
+ * Exports a bean as an array
+ * @param $bean
+ * @return array $arr
+ */
+ public function exportAsArr() {
+ $arr = array();
+ foreach($this->data as $prop=>$value) {
+ if ($value instanceof RedBean_Decorator){
+ $value = $value->getID();
+ }
+ $arr[ $prop ] = $value;
+
+ }
+ return $arr;
+ }
+
+ /**
+ * Finds another decorator
+ * @param $deco
+ * @param $filter
+ * @return array $decorators
+ */
+ public static function find( $deco, $filter, $start=0, $end=100, $orderby=" id ASC ", $extraSQL=false ) {
+
+ if (!is_array($filter)) {
+ return array();
+ }
+
+ if (count($filter)<1) {
+ return array();
+ }
+
+ //make all keys of the filter lowercase
+ $filters = array();
+ foreach($filter as $key=>$f) {
+ $filters[strtolower($key)] =$f;
+
+ if (!in_array($f,array("=","!=","<",">","<=",">=","like","LIKE"))) {
+ throw new ExceptionInvalidFindOperator();
+ }
+
+ }
+
+ $beans = RedBean_OODB::find( $deco->getData(), $filters, $start, $end, $orderby, $extraSQL );
+
+
+
+ $decos = array();
+ $dclass = PRFX.$deco->type.SFFX;
+ foreach( $beans as $bean ) {
+ $decos[ $bean->id ] = new $dclass( floatval( $bean->id ) );
+ $decos[ $bean->id ]->setData( $bean );
+ }
+ return $decos;
+ }
+
+
+}
+
View
33 RedBean/Driver.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Generic interface for Databases
+ */
+interface RedBean_Driver {
+
+ public static function getInstance( $host, $user, $pass, $dbname );
+
+ public function GetAll( $sql );
+
+ public function GetCol( $sql );
+
+ public function GetCell( $sql );
+
+ public function GetRow( $sql );
+
+ public function ErrorNo();
+
+ public function Errormsg();
+
+ public function Execute( $sql );
+
+ public function Escape( $str );
+
+ public function GetInsertID();
+
+ public function Affected_Rows();
+
+ public function setDebugMode( $tf );
+
+ public function GetRaw();
+
+}
View
157 RedBean/Driver/PDO.php
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * PDO support driver
+ * the PDO driver has been written by Desfrenes.
+ */
+class Redbean_Driver_PDO implements RedBean_Driver {
+ private static $instance;
+
+ private $debug = false;
+ private $pdo;
+ private $affected_rows;
+ private $rs;
+
+ public static function getInstance($dsn, $user, $pass, $dbname)
+ {
+ if(is_null(self::$instance))
+ {
+ self::$instance = new Redbean_Driver_PDO($dsn, $user, $pass);
+ }
+ return self::$instance;
+ }
+
+ public function __construct($dsn, $user, $pass)
+ {
+ $this->pdo = new PDO(
+ $dsn,
+ $user,
+ $pass,
+ array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC)
+ );
+ }
+
+ public function GetAll( $sql )
+ {
+ try{
+ if ($this->debug)
+ {
+ echo "<HR>" . $sql;
+ }
+ $rs = $this->pdo->query($sql);
+ $this->rs = $rs;
+ $rows = $rs->fetchAll();
+ if(!$rows)
+ {
+ $rows = array();
+ }
+
+ if ($this->debug)
+ {
+ if (count($rows) > 0)
+ {
+ echo "<br><b style='color:green'>resultset: " . count($rows) . " rows</b>";
+ }
+
+ $str = $this->Errormsg();
+ if ($str != "")
+ {
+ echo "<br><b style='color:red'>" . $str . "</b>";
+ }
+ }
+ }
+ catch(Exception $e){ return array(); }
+ return $rows;
+ }
+
+ public function GetCol($sql)
+ {
+ try{
+ $rows = $this->GetAll($sql);
+ $cols = array();
+
+ if ($rows && is_array($rows) && count($rows)>0){
+ foreach ($rows as $row)
+ {
+ $cols[] = array_shift($row);
+ }
+ }
+
+ }
+ catch(Exception $e){ return array(); }
+ return $cols;
+ }
+
+ public function GetCell($sql)
+ {
+ try{
+ $arr = $this->GetAll($sql);
+ $row1 = array_shift($arr);
+ $col1 = array_shift($row1);
+ }
+ catch(Exception $e){}
+ return $col1;
+ }
+
+ public function GetRow($sql)
+ {
+ try{
+ $arr = $this->GetAll($sql);
+ }
+ catch(Exception $e){ return array(); }
+ return array_shift($arr);
+ }
+
+ public function ErrorNo()
+ {
+ $infos = $this->pdo->errorInfo();
+ return $infos[1];
+ }
+ public function Errormsg()
+ {
+ $infos = $this->pdo->errorInfo();
+ return $infos[2];
+ }
+ public function Execute( $sql )
+ {
+ try{
+ if ($this->debug)
+ {
+ echo "<HR>" . $sql;
+ }
+ $this->affected_rows = $this->pdo->exec($sql);
+ if ($this->debug)
+ {
+ $str = $this->Errormsg();
+ if ($str != "")
+ {
+ echo "<br><b style='color:red'>" . $str . "</b>";
+ }
+ }
+ }
+ catch(Exception $e){ return 0; }
+ return $this->affected_rows;
+ }
+ public function Escape( $str )
+ {
+ return substr(substr($this->pdo->quote($str), 1), 0, -1);
+ }
+ public function GetInsertID()
+ {
+ return (int) $this->pdo->lastInsertId();
+ }
+ public function Affected_Rows()
+ {
+ return (int) $this->affected_rows;
+ }
+ public function setDebugMode( $tf )
+ {
+ $this->debug = (bool)$tf;
+ }
+ public function GetRaw()
+ {
+ return $this->rs;
+ }
+}
View
2 RedBean/Exception.php
@@ -0,0 +1,2 @@
+<?php
+class Redbean_Exception extends Exception{}
View
2 RedBean/Exception/FailedAccessBean.php
@@ -0,0 +1,2 @@
+<?php
+class ExceptionFailedAccessBean extends Exception{}
View
2 RedBean/Exception/InvalidArgument.php
@@ -0,0 +1,2 @@
+<?php
+class ExceptionInvalidArgument extends RedBean_Exception {}
View
2 RedBean/Exception/InvalidFindOperator.php
@@ -0,0 +1,2 @@
+<?php
+class ExceptionRedBeanSecurity extends RedBean_Exception {}
View
2 RedBean/Exception/InvalidParentChildCombination.php
@@ -0,0 +1,2 @@
+<?php
+class ExceptionInvalidParentChildCombination extends RedBean_Exception{}
View
2 RedBean/Exception/SQL.php
@@ -0,0 +1,2 @@
+<?php
+class ExceptionSQL extends RedBean_Exception {};
View
1,822 RedBean/OODB.php
@@ -0,0 +1,1822 @@
+<?php
+
+
+/**
+ * RedBean OODB (object oriented database) Core class for the RedBean ORM pack
+ * @author gabordemooij
+ *
+ */
+class RedBean_OODB {
+
+ /**
+ *
+ * @var float
+ */
+ private static $version = 0.5;
+
+ /**
+ *
+ * @var string
+ */
+ private static $versioninf = "
+ RedBean Object Database layer
+ VERSION 0.5
+ BY G.J.G.T DE MOOIJ
+ LICENSE BSD
+ COPYRIGHT 2009
+ ";
+
+ /**
+ * Indicates how long one can lock an item,
+ * defaults to ten minutes
+ * If a user opens a bean and he or she does not
+ * perform any actions on it others cannot modify the
+ * bean during this time interval.
+ * @var unknown_type
+ */
+ private static $locktime = 10;
+
+ /**
+ * a standard adapter for use with RedBean's MYSQL Database wrapper or
+ * ADO library
+ * @var RedBean_DBAdapter
+ */
+ public static $db;
+
+ /**
+ *
+ * @var boolean
+ */
+ private static $locking = true;
+
+ /**
+ *
+ * @var array all allowed sql types
+ */
+ public static $typeno_sqltype = array(
+ " TINYINT(3) UNSIGNED ",
+ " INT(11) UNSIGNED ",
+ " BIGINT(20) SIGNED ",
+ " VARCHAR(255) ",
+ " TEXT ",
+ " LONGTEXT "
+ );
+
+ /**
+ *
+ * @var array all allowed sql types
+ */
+ public static $sqltype_typeno = array(
+ "tinyint(3) unsigned"=>0,
+ "int(11) unsigned"=>1,
+ "bigint(20) signed"=>2,
+ "varchar(255)"=>3,
+ "text"=>4,
+ "longtext"=>5
+ );
+
+ /**
+ * @var array all dtype types
+ */
+ public static $dtypes = array(
+ "tintyintus","intus","ints","varchar255","text","ltext"
+ );
+
+ /**
+ *
+ * @var string $pkey - a fingerprint for locking
+ */
+ public static $pkey = false;
+
+ /**
+ *
+ * @var RedBean_OODB
+ */
+ private static $me = null;
+
+ /**
+ *
+ * Indicates the current engine
+ * @var string
+ */
+ private static $engine = "myisam";
+
+ /**
+ * @var boolean $frozen - indicates whether the db may be adjusted or not
+ */
+ private static $frozen = false;
+
+
+ /**
+ * Closes and unlocks the bean
+ * @return unknown_type
+ */
+ public function __destruct() {
+
+
+ //prepare database
+ if (self::$engine === "innodb") {
+ self::$db->exec("COMMIT");
+ }
+ else if (self::$engine === "myisam"){
+ //nope
+ }
+ RedBean_OODB::releaseAllLocks();
+
+ }
+
+ /**
+ * Returns the version information of this RedBean instance
+ * @return float
+ */
+ public static function getVersionInfo() {
+ return self::$versioninf;
+ }
+
+ /**
+ * Returns the version number of this RedBean instance
+ * @return unknown_type
+ */
+ public static function getVersionNumber() {
+ return self::$version;
+ }
+
+ /**
+ * Toggles Forward Locking
+ * @param $tf
+ * @return unknown_type
+ */
+ public static function setLocking( $tf ) {
+ self::$locking = $tf;
+ }
+
+
+ /**
+ * Gets the current locking mode (on or off)
+ * @return unknown_type
+ */
+ public static function getLocking() {
+ return self::$locking;
+ }
+
+
+ /**
+ * Toggles optimizer
+ * @param $bool
+ * @return unknown_type
+ */
+ public static function setOptimizerActive( $bool ) {
+ self::$optimizer = (boolean) $bool;
+ }
+
+ /**
+ * Returns state of the optimizer
+ * @param $bool
+ * @return unknown_type
+ */
+ public static function getOptimizerActive() {
+ return self::$optimizer;
+ }
+
+ /**
+ * Checks whether a bean is valid
+ * @param $bean
+ * @return unknown_type
+ */
+ public static function checkBean(OODBBean $bean) {
+
+ foreach($bean as $prop=>$value) {
+ $prop = preg_replace('/[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_]/',"",$prop);
+ if (strlen(trim($prop))===0) {
+ throw new ExceptionRedBeanSecurity("Invalid Characters in property");
+ }
+ else {
+
+ $bean->$prop = $value;
+ }
+ }
+
+ //has redBean already been initialized?
+ if (!self::$pkey) self::init();
+
+ //Is the bean valid? does the bean have an id?
+ if (!isset($bean->id)) {
+ throw new Exception("Invalid bean, no id");
+ }
+
+ //is the id numeric?
+ if (!is_numeric($bean->id)) {
+ throw new Exception("Invalid bean, id not numeric");
+ }
+
+ //does the bean have a type?
+ if (!isset($bean->type)) {
+ throw new Exception("Invalid bean, no type");
+ }
+
+ //is the beantype correct and valid?
+ if (!is_string($bean->type) || is_numeric($bean->type) || strlen($bean->type)<3) {
+ throw new Exception("Invalid bean, wrong type");
+ }
+
+ //is the beantype legal?
+ if ($bean->type==="locking" || $bean->type==="dtyp") {
+ throw new Exception("Beantype is reserved table");
+ }
+
+ //is the beantype allowed?
+ if (strpos($bean->type,"_")!==false && ctype_alnum($bean->type)) {
+ throw new Exception("Beantype contains illegal characters");
+ }
+
+
+ }
+
+ /**
+ * same as check bean, but does additional checks for associations
+ * @param $bean
+ * @return unknown_type
+ */
+ public static function checkBeanForAssoc( $bean ) {
+
+ //check the bean
+ self::checkBean($bean);
+
+ //make sure it has already been saved to the database, else we have no id.
+ if (intval($bean->id) < 1) {
+ //if it's not saved, save it
+ $bean->id = self::set( $bean );
+ }
+
+ return $bean;
+
+ }
+
+ /**
+ * Returns the current engine
+ * @return unknown_type
+ */
+ public static function getEngine() {
+ return self::$engine;
+ }
+
+ /**
+ * Sets the current engine
+ * @param $engine
+ * @return unknown_type
+ */
+ public static function setEngine( $engine ) {
+
+ if ($engine=="myisam" || $engine=="innodb") {
+ self::$engine = $engine;
+ }
+ else {
+ throw new Exception("Unsupported database engine");
+ }
+
+ return self::$engine;
+
+ }
+
+ /**
+ * Inserts a bean into the database
+ * @param $bean
+ * @return $id
+ */
+ public static function set( OODBBean $bean ) {
+
+ self::checkBean($bean);
+
+
+ $db = self::$db; //I am lazy, I dont want to waste characters...
+
+
+ $table = $db->escape($bean->type); //what table does it want
+
+ //may we adjust the database?
+ if (!self::$frozen) {
+
+ //does this table exist?
+ $tables = self::showTables();
+
+ if (!in_array($table, $tables)) {
+
+ if (self::$engine=="myisam") {
+ //this fellow has no table yet to put his beer on!
+ $createtableSQL = "
+ CREATE TABLE `$table` (
+ `id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
+ PRIMARY KEY ( `id` )
+ ) ENGINE = MYISAM
+ ";
+ }
+ else {
+ $createtableSQL = "
+ CREATE TABLE `$table` (
+ `id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
+ PRIMARY KEY ( `id` )
+ ) ENGINE = InnoDB
+ ";
+ }
+ //get a table for our friend!
+ $db->exec( $createtableSQL );
+
+
+ //jupz, now he has its own table!
+ self::addTable( $table );
+ }
+
+ //does the table fit?
+ $columnsRaw = $db->get("describe `$table` ");
+
+ $columns = array();
+ foreach($columnsRaw as $r) {
+ $columns[$r["Field"]]=$r["Type"];
+ }
+
+ $insertvalues = array();
+ $insertcolumns = array();
+ $updatevalues = array();
+
+ foreach( $bean as $p=>$v) {
+ if ($p!="type" && $p!="id") {
+ $p = $db->escape($p);
+ $v = $db->escape($v);
+ //What kind of property are we dealing with?
+ $typeno = self::inferType($v);
+ //Is this property represented in the table?
+ if (isset($columns[$p])) {
+ //yes it is, does it still fit?
+ $sqlt = self::getType($columns[$p]);
+ //echo "TYPE = $sqlt .... $typeno ";
+ if ($typeno > $sqlt) {
+ //no, we have to widen the database column type
+ $changecolumnSQL="ALTER TABLE `$table` CHANGE `$p` `$p` ".self::$typeno_sqltype[$typeno];
+ $db->exec( $changecolumnSQL );
+ }
+ }
+ else {
+ //no it is not
+ $addcolumnSQL = "ALTER TABLE `$table` ADD `$p` ".self::$typeno_sqltype[$typeno];
+ $db->exec( $addcolumnSQL );
+ }
+ //Okay, now we are sure that the property value will fit
+ $insertvalues[] = "\"".$v."\"";
+ $insertcolumns[] = "`".$p."`";
+ $updatevalues[] = " `$p`=\"$v\" ";
+ }
+ }
+
+ }
+ else {
+
+ foreach( $bean as $p=>$v) {
+ if ($p!="type" && $p!="id") {
+ $p = $db->escape($p);
+ $v = $db->escape($v);
+
+ $insertvalues[] = "\"".$v."\"";
+ $insertcolumns[] = "`".$p."`";
+ $updatevalues[] = " `$p`=\"$v\" ";
+ }
+ }
+
+ }
+
+ //Does the record exist already?
+ if ($bean->id) {
+ //echo "<hr>Now trying to open bean....";
+ self::openBean($bean, true);
+ //yes it exists, update it
+ if (count($updatevalues)>0) {
+ $updateSQL = "UPDATE `$table` SET ".implode(",",$updatevalues)."WHERE id = ".$bean->id;
+ //execute the previously build query
+ $db->exec( $updateSQL );
+ }
+ }
+ else {
+ //no it does not exist, create it
+ if (count($insertvalues)>0) {
+ $insertSQL = "INSERT INTO `$table` ";
+ $insertSQL .= " ( id, ".implode(",",$insertcolumns)." ) ";
+ $insertSQL .= " VALUES( null, ".implode(",",$insertvalues)." ) ";
+ }
+ else {
+ $insertSQL = "INSERT INTO `$table` VALUES(null) ";
+ }
+ //execute the previously build query
+ $db->exec( $insertSQL );
+ $bean->id = $db->getInsertID();
+ self::openBean($bean);
+ }
+
+ return $bean->id;
+
+ }
+
+
+ /**
+ * Infers the SQL type of a bean
+ * @param $v
+ * @return $type the SQL type number constant
+ */
+ public static function inferType( $v ) {
+ $db = self::$db;
+ $rawv = $v;
+ $v = "'".$db->escape(strval($v))."'";
+ $checktypeSQL = "insert into dtyp VALUES(null,$v,$v,$v,$v,$v )";
+ $db->exec( $checktypeSQL );
+ $id = $db->getInsertID();
+ $readtypeSQL = "select tinyintus,intus,ints,varchar255,`text` from dtyp where id=$id";
+ $row=$db->getRow($readtypeSQL);
+ $db->exec("truncate table dtyp");
+ $tp = 0;
+ foreach($row as $t=>$tv) {
+ if (strval($tv) === strval($rawv)) {
+ return $tp;
+ }
+ $tp++;
+ }
+ return $tp;
+ }
+
+ /**
+ * Returns the RedBean type const for an SQL type
+ * @param $sqlType
+ * @return $typeno
+ */
+ public static function getType( $sqlType ) {
+
+ if (in_array($sqlType,self::$sqltype_typeno)) {
+ $typeno = self::$sqltype_typeno[$sqlType];
+ }
+ else {
+ $typeno = -1;
+ }
+
+ return $typeno;
+ }
+
+ /**
+ * Initializes RedBean
+ * @return bool $true
+ */
+ public static function init( $dontclose = false) {
+
+ self::$me = new RedBean_OODB();
+
+
+ //prepare database
+ if (self::$engine === "innodb") {
+ self::$db->exec("SET autocommit=0");
+ self::$db->exec("START TRANSACTION");
+ }
+ else if (self::$engine === "myisam"){
+ self::$db->exec("SET autocommit=1");
+ }
+
+
+ //generate the basic redbean tables
+ //Create the RedBean tables we need -- this should only happen once..
+ if (!self::$frozen) {
+
+ self::$db->exec("drop tables dtyp");
+
+ self::$db->exec("
+ CREATE TABLE IF NOT EXISTS `dtyp` (
+ `id` int(11) unsigned NOT NULL auto_increment,
+ `tinyintus` tinyint(3) unsigned NOT NULL,
+ `intus` int(11) unsigned NOT NULL,
+ `ints` bigint(20) NOT NULL,
+
+ `varchar255` varchar(255) NOT NULL,
+ `text` text NOT NULL,
+ PRIMARY KEY (`id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
+ ");
+
+ self::$db->exec("
+ CREATE TABLE IF NOT EXISTS `locking` (
+ `tbl` varchar(255) NOT NULL,
+ `id` bigint(20) NOT NULL,
+ `fingerprint` varchar(255) NOT NULL,
+ `expire` int(11) NOT NULL,
+ UNIQUE KEY `tbl` (`tbl`,`id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+ ");
+
+ //rbt
+ self::$db->exec("
+ CREATE TABLE IF NOT EXISTS `redbeantables` (
+ `id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
+ `tablename` VARCHAR( 255 ) NOT NULL ,
+ PRIMARY KEY ( `id` ),
+ UNIQUE KEY `tablename` (`tablename`)
+ ) ENGINE = MYISAM
+ ");
+
+ self::$db->exec("
+ CREATE TABLE IF NOT EXISTS `searchindex` (
+ `id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
+ `ind` VARCHAR( 255 ) NOT NULL ,
+ `cnt` INT( 11 ) NOT NULL ,
+ PRIMARY KEY ( `id` ),
+ UNIQUE KEY `ind` (`ind`)
+ ) ENGINE = MYISAM ");
+
+
+
+
+ }
+
+ //generate a key
+ if (!self::$pkey) {
+ self::$pkey = str_replace(".","",microtime(true)."".mt_rand());
+ }
+
+ return true;
+ }
+
+ /**
+ * Freezes the database so it won't be changed anymore
+ * @return unknown_type
+ */
+ public static function freeze() {
+ self::$frozen = true;
+ }
+
+ /**
+ * UNFreezes the database so it won't be changed anymore
+ * @return unknown_type
+ */
+ public static function unfreeze() {
+ self::$frozen = false;
+ }
+
+ /**
+ * Returns all redbean tables or all tables in the database
+ * @param $all if set to true this function returns all tables instead of just all rb tables
+ * @return array $listoftables
+ */
+ public static function showTables( $all=false ) {
+
+ $db = self::$db;
+
+ if ($all && self::$frozen) {
+ $alltables = $db->getCol("show tables");
+ return $alltables;
+ }
+ else {
+ $alltables = $db->getCol("select tablename from redbeantables");
+ return $alltables;
+ }
+
+ }
+
+ /**
+ * Registers a table with RedBean
+ * @param $tablename
+ * @return void
+ */
+ public static function addTable( $tablename ) {
+
+ $db = self::$db;
+
+ $tablename = $db->escape( $tablename );
+
+ $db->exec("replace into redbeantables values (null, \"$tablename\") ");
+
+ }
+
+ /**
+ * UNRegisters a table with RedBean
+ * @param $tablename
+ * @return void
+ */
+ public static function dropTable( $tablename ) {
+
+ $db = self::$db;
+
+ $tablename = $db->escape( $tablename );
+
+ $db->exec("delete from redbeantables where tablename = \"$tablename\" ");
+
+
+ }
+
+ /**
+ * Quick and dirty way to release all locks
+ * @return unknown_type
+ */
+ public function releaseAllLocks() {
+
+ self::$db->exec("DELETE FROM locking WHERE fingerprint=\"".self::$pkey."\" ");
+
+ }
+
+
+ /**
+ * Opens and locks a bean
+ * @param $bean
+ * @return unknown_type
+ */
+ public static function openBean( $bean, $mustlock=false) {
+
+ self::checkBean( $bean );
+
+ //echo "trying to open bean... ".print_r($bean,1);
+
+ //If locking is turned off, or the bean has no persistance yet (not shared) life is always a success!
+ if (!self::$locking || $bean->id === 0) return true;
+
+ $db = self::$db;
+
+ //remove locks that have been expired...
+ $removeExpiredSQL = "DELETE FROM locking WHERE expire < ".(time()-self::$locktime);
+ $db->exec($removeExpiredSQL);
+
+ $tbl = $db->escape( $bean->type );
+ $id = intval( $bean->id );
+
+ //Is the bean already opened for us?
+ $checkopenSQL = "SELECT id FROM locking WHERE id=$id AND tbl=\"$tbl\" AND fingerprint=\"".self::$pkey."\" ";
+ $row = $db->getRow($checkopenSQL);
+ if ($row && is_array($row) && count($row)>0) {
+ $updateexpstamp = "UPDATE locking SET expire=".time()." WHERE id =".$row["id"];
+ return true; //bean is locked for us!
+ }
+
+ //If you must lock a bean then the bean must have been locked by a previous call.
+ if ($mustlock) {
+ throw new ExceptionFailedAccessBean("Could not acquire a lock for bean $tbl . $id ");
+ return false;
+ }
+
+ //try to get acquire lock on the bean
+ $openSQL = "INSERT INTO locking VALUES(\"$tbl\",$id,\"".self::$pkey."\",\"".time()."\") ";
+ $trials = 0;
+ $aff = 0;
+ while( $aff < 1 && $trials < 5 ) {
+ $db->exec($openSQL);
+ $aff = $db->getAffectedRows();
+ $trials++;
+ if ($aff < 1) usleep(500000); //half a sec
+ }
+
+ if ($trials > 4) {
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+
+ /**
+ * For internal use, synchronizes a block of code
+ * @param $toggle
+ * @return unknown_type
+ */
+ private static function sync( $toggle ) {
+
+ $bean = RedBean_OODB::dispense("_syncmethod");
+ $bean->id = 0;
+
+ if ($toggle) {
+ self::openBean( $bean );
+ }
+ else {
+ self::closeBean( $bean );
+ }
+ }
+
+ /**
+ * Gets a bean by its primary ID
+ * @param $type
+ * @param $id
+ * @return OODBBean $bean
+ */
+ public static function getById($type, $id, $data=false) {
+
+ $bean = self::dispense( $type );
+ $db = self::$db;
+ $table = $db->escape( $type );
+ $id = intval( $id );
+ $bean->id = $id;
+
+ //try to open the bean
+ self::openBean($bean);
+
+ //load the bean using sql
+ if (!$data) {
+ $getSQL = "SELECT * FROM `$type` WHERE id = $id ";
+ $row = $db->getRow( $getSQL );
+ }
+ else {
+ $row = $data;
+ }
+
+ if ($row && is_array($row) && count($row)>0) {
+ foreach($row as $p=>$v) {
+ //populate the bean with the database row
+ $bean->$p = $v;
+ }
+ }
+ else {
+ throw new ExceptionFailedAccessBean("bean not found");
+ }
+
+ return $bean;
+
+ }
+
+ /**
+ * Checks whether a type-id combination exists
+ * @param $type
+ * @param $id
+ * @return unknown_type
+ */
+ public static function exists($type,$id) {
+
+ $db = self::$db;
+ $id = intval( $id );
+ $type = $db->escape( $type );
+
+ //$alltables = $db->getCol("show tables");
+ $alltables = self::showTables();
+
+ if (!in_array($type, $alltables)) {
+ return false;
+ }
+ else {
+ $no = $db->getCell("select count(*) from `$type` where id=$id");
+ if (intval($no)) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Counts occurences of a bean
+ * @param $type
+ * @return integer $i
+ */
+ public static function numberof($type) {
+
+ $db = self::$db;
+ $type = strtolower( $db->escape( $type ) );
+
+ $alltables = self::showTables();
+
+ if (!in_array($type, $alltables)) {
+ return 0;
+ }
+ else {
+ $no = $db->getCell("select count(*) from `$type`");
+ return $no;
+ }
+ }
+
+ /**
+ * Gets all beans of $type, grouped by $field.
+ *
+ * @param String Object type e.g. "user" (lowercase!)
+ * @param String Field/parameter e.g. "zip"
+ * @return Array list of beans with distinct values of $field. Uses GROUP BY
+ * @author Alan J. Hogan
+ **/
+ static function distinct($type, $field)
+ {
+ //TODO: Consider if GROUP BY (equivalent meaning) is more portable
+ //across DB types?
+ $db = self::$db;
+ $type = strtolower( $db->escape( $type ) );
+ $field = $db->escape( $field );
+
+ $alltables = self::showTables();
+
+ if (!in_array($type, $alltables)) {
+ return array();
+ }
+ else {
+ $ids = $db->getCol("SELECT id FROM `$type` GROUP BY $field");
+ $beans = array();
+ if (is_array($ids) && count($ids)>0) {
+ foreach( $ids as $id ) {
+ $beans[ $id ] = self::getById( $type, $id , false);
+ }
+ }
+ return $beans;
+ }
+ }
+
+ /**
+ * Simple statistic
+ * @param $type
+ * @param $field
+ * @return integer $i
+ */
+ private static function stat($type,$field,$stat="sum") {
+
+ $db = self::$db;
+ $type = strtolower( $db->escape( $type ) );
+ $field = strtolower( $db->escape( $field ) );
+ $stat = $db->escape( $stat );
+
+ $alltables = self::showTables();
+
+ if (!in_array($type, $alltables)) {
+ return 0;
+ }
+ else {
+ $no = $db->getCell("select $stat(`$field`) from `$type`");
+ return $no;
+ }
+ }
+
+ /**
+ * Sum
+ * @param $type
+ * @param $field
+ * @return float $i
+ */
+ public static function sumof($type,$field) {
+ return self::stat( $type, $field, "sum");
+ }
+
+ /**
+ * AVG
+ * @param $type
+ * @param $field
+ * @return float $i
+ */
+ public static function avgof($type,$field) {
+ return self::stat( $type, $field, "avg");
+ }
+
+ /**
+ * minimum
+ * @param $type
+ * @param $field
+ * @return float $i
+ */
+ public static function minof($type,$field) {
+ return self::stat( $type, $field, "min");
+ }
+
+ /**
+ * maximum
+ * @param $type
+ * @param $field
+ * @return float $i
+ */
+ public static function maxof($type,$field) {
+ return self::stat( $type, $field, "max");
+ }
+
+
+ /**
+ * Unlocks everything
+ * @return unknown_type
+ */
+ public static function resetAll() {
+ $sql = "TRUNCATE locking";
+ self::$db->exec( $sql );
+ return true;
+ }
+
+
+ public static function processQuerySlots($sql, $slots) {
+
+ $db = self::$db;
+
+ //Just a funny code to identify slots based on randomness
+ $code = sha1(rand(1,1000)*time());
+
+ //This ensures no one can hack our queries via SQL template injection
+ foreach( $slots as $key=>$value ) {
+ $sql = str_replace( "{".$key."}", "{".$code.$key."}" ,$sql );
+ }
+
+ //replace the slots inside the SQL template
+ foreach( $slots as $key=>$value ) {
+ $sql = str_replace( "{".$code.$key."}", "\"".$db->escape( $value )."\"",$sql );
+ }
+
+ return $sql;
+ }
+
+ public static function fastLoader( $type, $ids ) {
+
+ $db = self::$db;
+ $sql = "SELECT * FROM `$type` WHERE id IN ( ".implode(",", $ids)." ) ORDER BY FIELD(id,".implode(",", $ids).") ASC
+ ";
+ return $db->get( $sql );
+
+ }
+
+ public static function getBySQL( $rawsql, $slots, $table ) {
+
+ $db = self::$db;
+ $sql = $rawsql;
+
+ if (is_array($slots)) {
+ $sql = self::processQuerySlots( $sql, $slots );
+ }
+
+ $rs = $db->getCol( "select `$table`.id from $table where " . $sql );
+
+ if (is_array($rs)) {
+ return $rs;
+ }
+ else {
+ return array();
+ }
+ }
+
+
+ /**
+ * Finds a bean using search parameters
+ * @param $bean
+ * @param $searchoperators
+ * @param $start
+ * @param $end
+ * @param $orderby
+ * @return unknown_type
+ */
+ public static function find(OODBBean $bean, $searchoperators = array(), $start=0, $end=100, $orderby="id ASC", $extraSQL=false) {
+
+ self::checkBean( $bean );
+ $db = self::$db;
+ $tbl = $db->escape( $bean->type );
+
+ $findSQL = "SELECT id FROM `$tbl` WHERE ";
+
+
+ foreach($bean as $p=>$v) {
+ if ($p === "type" || $p === "id") continue;
+ $p = $db->escape($p);
+ $v = $db->escape($v);
+ if (isset($searchoperators[$p])) {
+
+ if ($searchoperators[$p]==="LIKE") {
+ $part[] = " `$p`LIKE \"%$v%\" ";
+ }
+ else {
+ $part[] = " `$p` ".$searchoperators[$p]." \"$v\" ";
+ }
+ }
+ else {
+
+ }
+ }
+
+ if ($extraSQL) {
+ $findSQL .= @implode(" AND ",$part) . $extraSQL;
+ }
+ else {
+ $findSQL .= @implode(" AND ",$part) . " ORDER BY $orderby LIMIT $start, $end ";
+ }
+
+
+ $ids = $db->getCol( $findSQL );
+ $beans = array();
+
+ if (is_array($ids) && count($ids)>0) {
+ foreach( $ids as $id ) {
+ $beans[ $id ] = self::getById( $bean->type, $id , false);
+ }
+ }
+
+ return $beans;
+
+ }
+
+
+ /**
+ * Returns a plain and simple array filled with record data
+ * @param $type
+ * @param $start
+ * @param $end
+ * @param $orderby
+ * @return unknown_type
+ */
+ public static function listAll($type, $start=false, $end=false, $orderby="id ASC", $extraSQL = false) {
+
+ $db = self::$db;
+
+ if ($extraSQL) {
+
+ $listSQL = "SELECT * FROM ".$db->escape($type)." ".$extraSQL;
+
+ }
+ else {
+
+ $listSQL = "SELECT * FROM ".$db->escape($type)."
+ ORDER BY ".$orderby;
+
+ if ($end !== false && $start===false) {
+ $listSQL .= " LIMIT ".intval($end);
+ }
+
+ if ($start !== false && $end !== false) {
+ $listSQL .= " LIMIT ".intval($start).", ".intval($end);
+ }
+
+ if ($start !== false && $end===false) {
+ $listSQL .= " LIMIT ".intval($start).", 18446744073709551615 ";
+ }
+
+
+
+ }
+
+ return $db->get( $listSQL );
+
+ }
+
+
+ /**
+ * Associates two beans
+ * @param $bean1
+ * @param $bean2
+ * @return unknown_type
+ */
+ public static function associate( OODBBean $bean1, OODBBean $bean2 ) { //@associate
+
+ //get a database
+ $db = self::$db;
+
+ //first we check the beans whether they are valid
+ $bean1 = self::checkBeanForAssoc($bean1);
+ $bean2 = self::checkBeanForAssoc($bean2);
+
+ self::openBean( $bean1, true );
+ self::openBean( $bean2, true );
+
+ //sort the beans
+ $tp1 = $bean1->type;
+ $tp2 = $bean2->type;
+ if ($tp1==$tp2){
+ $arr = array( 0=>$bean1, 1 =>$bean2 );
+ }
+ else {
+ $arr = array( $tp1=>$bean1, $tp2 =>$bean2 );
+ }
+ ksort($arr);
+ $bean1 = array_shift( $arr );
+ $bean2 = array_shift( $arr );
+
+ $id1 = intval($bean1->id);
+ $id2 = intval($bean2->id);
+
+ //infer the association table
+ $tables = array();
+ array_push( $tables, $db->escape( $bean1->type ) );
+ array_push( $tables, $db->escape( $bean2->type ) );
+ //sort the table names to make sure we only get one assoc table
+ sort($tables);
+ $assoctable = $db->escape( implode("_",$tables) );
+
+ //check whether this assoctable already exists
+ if (!self::$frozen) {
+ $alltables = self::showTables();
+ if (!in_array($assoctable, $alltables)) {
+ //no assoc table does not exist, create it..
+ $t1 = $tables[0];
+ $t2 = $tables[1];
+
+ if ($t1==$t2) {
+ $t2.="2";
+ }
+
+ $assoccreateSQL = "
+ CREATE TABLE `$assoctable` (
+ `id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
+ `".$t1."_id` INT( 11 ) UNSIGNED NOT NULL,
+ `".$t2."_id` INT( 11 ) UNSIGNED NOT NULL,