Skip to content
Snippets Groups Projects
class.orm.php 71.5 KiB
Newer Older
  • Learn to ignore specific revisions
  •         // XXX: Assuming schema is not changing — add support to track
            //      current schema
            if (isset($cache[$table]))
                return $cache[$table];
    
            $sql = 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS '
    
                .'WHERE TABLE_NAME = '.db_input($table).' AND TABLE_SCHEMA = DATABASE() '
    
                .'ORDER BY ORDINAL_POSITION';
    
            $ex = new MysqlExecutor($sql, array());
    
            $columns = array();
            while (list($column) = $ex->getRow()) {
                $columns[] = $column;
            }
            return $cache[$table] = $columns;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    }
    
    class MysqlExecutor {
    
        var $stmt;
        var $fields = array();
        var $sql;
        var $params;
    
        // Array of [count, model] values representing which fields in the
        // result set go with witch model.  Useful for handling select_related
        // queries
        var $map;
    
        function __construct($sql, $params, $map=null) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            $this->sql = $sql;
            $this->params = $params;
    
            $this->map = $map;
        }
    
        function getMap() {
            return $this->map;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function _prepare() {
    
            $this->execute();
            $this->_setup_output();
        }
    
        function execute() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            if (!($this->stmt = db_prepare($this->sql)))
                throw new Exception('Unable to prepare query: '.db_error()
                    .' '.$this->sql);
            if (count($this->params))
                $this->_bind($this->params);
    
            if (!$this->stmt->execute() || ! $this->stmt->store_result()) {
    
                throw new OrmException('Unable to execute query: ' . $this->stmt->error);
            }
            return true;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function _bind($params) {
            if (count($params) != $this->stmt->param_count)
    
                throw new Exception(__('Parameter count does not match query'));
    
    Jared Hancock's avatar
    Jared Hancock committed
    
            $types = '';
            $ps = array();
    
            foreach ($params as &$p) {
    
                if (is_int($p) || is_bool($p))
    
    Jared Hancock's avatar
    Jared Hancock committed
                    $types .= 'i';
    
                elseif (is_float($p))
                    $types .= 'd';
    
    Jared Hancock's avatar
    Jared Hancock committed
                elseif (is_string($p))
                    $types .= 's';
    
                // TODO: Emit error if param is null
    
    Jared Hancock's avatar
    Jared Hancock committed
                $ps[] = &$p;
            }
    
    Jared Hancock's avatar
    Jared Hancock committed
            array_unshift($ps, $types);
            call_user_func_array(array($this->stmt,'bind_param'), $ps);
        }
    
        function _setup_output() {
    
            if (!($meta = $this->stmt->result_metadata()))
                throw new OrmException('Unable to fetch statment metadata: ', $this->stmt->error);
    
            $this->fields = $meta->fetch_fields();
    
            $meta->free_result();
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        // Iterator interface
        function rewind() {
            if (!isset($this->stmt))
                $this->_prepare();
            $this->stmt->data_seek(0);
        }
    
        function next() {
            $status = $this->stmt->fetch();
            if ($status === false)
    
                throw new OrmException($this->stmt->error);
    
    Jared Hancock's avatar
    Jared Hancock committed
            elseif ($status === null) {
                $this->close();
                return false;
            }
            return true;
        }
    
        function getArray() {
            $output = array();
            $variables = array();
    
            if (!isset($this->stmt))
                $this->_prepare();
    
            foreach ($this->fields as $f)
                $variables[] = &$output[$f->name]; // pass by reference
    
    
            if (!call_user_func_array(array($this->stmt, 'bind_result'), $variables))
                throw new OrmException('Unable to bind result: ' . $this->stmt->error);
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            if (!$this->next())
                return false;
            return $output;
        }
    
    
        function getRow() {
            $output = array();
            $variables = array();
    
            if (!isset($this->stmt))
                $this->_prepare();
    
            foreach ($this->fields as $f)
                $variables[] = &$output[]; // pass by reference
    
    
            if (!call_user_func_array(array($this->stmt, 'bind_result'), $variables))
                throw new OrmException('Unable to bind result: ' . $this->stmt->error);
    
    
            if (!$this->next())
                return false;
            return $output;
        }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
        function close() {
            if (!$this->stmt)
                return;
    
            $this->stmt->close();
            $this->stmt = null;
        }
    
    
        function affected_rows() {
            return $this->stmt->affected_rows;
        }
    
        function insert_id() {
            return $this->stmt->insert_id;
        }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
        function __toString() {
    
            $self = $this;
            $x = 0;
            return preg_replace_callback('/\?/', function($m) use ($self, &$x) {
                $p = $self->params[$x++];
                return db_real_escape($p, is_string($p));
            }, $this->sql);
    
    class Q implements Serializable {
    
        const NEGATED = 0x0001;
        const ANY =     0x0002;
    
        var $constraints;
        var $flags;
        var $negated = false;
        var $ored = false;
    
    
        function __construct($filter=array(), $flags=0) {
    
            if (!is_array($filter))
                $filter = array($filter);
    
            $this->constraints = $filter;
            $this->negated = $flags & self::NEGATED;
            $this->ored = $flags & self::ANY;
        }
    
        function isNegated() {
            return $this->negated;
        }
    
        function isOred() {
            return $this->ored;
        }
    
        function negate() {
            $this->negated = !$this->negated;
            return $this;
        }
    
    
        function union() {
            $this->ored = true;
        }
    
        function add($constraints) {
            if (is_array($constraints))
                $this->constraints = array_merge($this->constraints, $constraints);
            elseif ($constraints instanceof static)
                $this->constraints[] = $constraints;
            else
                throw new InvalidArgumentException('Expected an instance of Q or an array thereof');
            return $this;
        }
    
    
        static function not(array $constraints) {
            return new static($constraints, self::NEGATED);
        }
    
        static function any(array $constraints) {
    
            return new static($constraints, self::ANY);
    
    
        function serialize() {
            return serialize(array(
                'f' =>
                    ($this->negated ? self::NEGATED : 0)
                  | ($this->ored ? self::ANY : 0),
                'c' => $this->constraints
            ));
        }
    
        function unserialize($data) {
            $data = unserialize($data);
            $this->constraints = $data['c'];
            $this->ored = $data['f'] & self::ANY;
            $this->negated = $data['f'] & self::NEGATED;
        }