<?php

  
abstract class _S {
    
/**
     * Attached _S objects with operators
     *
     * @var _S
     */
    
protected $child;

    
/**
     * Defines what field of the database should be
     * referenced
     *
     * @var string
     */
    
protected $field;

    
/**
     * The modified for this field reference
     *
     * @var char
     */
    
protected $modifier;

    public function 
__construct($field) {
      
$this->field $field;
    }

    
/**
     * Store child element of linked list
     *
     * @param _S $child
     */
    
protected function setChild($child) {
      
$this->child $child;
    }

    
/**
     * Store the modifier (either &, | for _Q, or
     * +, -, *, / for _F)
     *
     * @param char $modifier
     */
    
protected function setModifier($modifier) {
      
$this->modifier .= $modifier;
    }

    
/**
     * Create a linked list of _F objects
     *
     * @param _F|int $f
     * @param char $modifier
     * @return _F
     */
    
protected function add($s$modifier) {
      
$s->setModifier($modifier);
      
$s->setChild($this);
      return 
$s;
    }

  }

  function 
F($field) {
    return new 
_F($field);
  }

  
/**
   * TODO: Fix more complicated statements such as
   * F('make') + F('model') * 3
   */
  
class _F extends _S {

    
/**
     * Add new object as linked list element along with it's
     * math operator
     *
     * @param _F $f
     * @param char $modifier
     * @return _F
     */
    
public function add($f$modifier) {
      if (!(
$f instanceof _F))
        
$f = new _F($f);
      return 
parent::add($f$modifier);
    }

    
/**
     * + operator overloading
     *
     * @return _F
     */
    
public function __add($f) {
      return 
$this->add($f"+");
    }

    
/**
     * - operator overloading
     *
     * @return _F
     */
    
public function __sub($f) {
      return 
$this->add($f"-");
    }

    
/**
     * * operator overloading
     *
     * @return _F
     */
    
public function __mul($f) {
      return 
$this->add($f"*");
    }

    
/**
     * / operator overloading
     *
     * @return _F
     */
    
public function __div($f) {
      return 
$this->add($f"/");
    }

    public function 
create_where() {
      
$stmt "";

      if (
$this->child)
        
$stmt .= $this->child->create_where();

      if (
$this->modifier$stmt .= {$this->modifier} ";

      
$stmt .= $this->field;

      return 
$stmt;
    }

  }

  function 
Q($arg) {
    return new 
_Q($arg);
  }

  class 
_Q extends _S {

    protected 
$not false;

    
/**
     * Create new _Q object with given argument/test
     *
     * @param array $arg
     */
    
public function __construct($arg) {
      if (!
is_array($arg) || sizeof($arg) > 1)
        throw new 
Exception("Invalid Q argument: " var_export($argtrue));
      
parent::__construct($arg);
    }

    
/**
     * Use this _Q object and it's child _Q object to create
     * a chained statement
     *
     * @return string
     */
    
public function create_where() {
      
$stmt "";

      
// prepend child statement
      
if ($this->child)
        
$stmt .= $this->child->create_where();

      
// use 'AND' or 'OR' modifier if applicable (when chained)
      
switch($this->modifier) {
        case 
"&":
          
$stmt .= " AND ";
          break;
        case 
"|":
          
$stmt .= " OR ";
          break;
      }

      
// use "NOT" modifier if specified using ~ operator
      
if ($this->not$stmt .= " NOT ";

      
$stmt .= Query::format_argument($this->field);

      return 
$stmt;
    }

    
/**
     * | operator overloading
     *
     * @param array $q
     * @return _Q
     */
    
public function __bw_or($q) {
      return 
$this->add($q"|");
    }

    
/**
     * & operator overloading
     *
     * @param array $q
     * @return _Q
     */
    
public function __bw_and($q) {
      return 
$this->add($q"&");
    }

    
/**
     * ~ operator overloading
     *
     * @return _Q
     */
    
public function __bw_not() {
      
$this->not true;
      return 
$this;
    }

  }