Login   Register  
PHP Classes
elePHPant
Icontem

File: SODA/SODA.inc.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Salvan Grégory  >  SODA  >  SODA/SODA.inc.php  >  Download  
File: SODA/SODA.inc.php
Role: Class source
Content type: text/plain
Description: main file
Class: SODA
Database abstraction layer that encrypt data
Author: By
Last change: New version 1.1:
Now you can change the type of database on the fly by setting the property _dbtype (change the driver)
More security with methods lock and unlock used to export the object and import it only if the file base.inc.php is the same as it creator.
Date: 6 years ago
Size: 25,786 bytes
 

Contents

Class file image Download
<?php

/** 
 * Main File
 * @package SODA
 * @ignore
*/

ini_set('display_errors',1);
error_reporting(E_ALL);


/** 
 * Main class: encrypt properties, load the right driver, import or export the encrypted object
 * @package SODA
 * @subpackage SODA
 * @author Salvan Gregory <dev@europeaweb.com>
 * @version 1.1
 * @copyright Copyright (c) 2008, Salvan Gregory europeaweb.com
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 * @property array $protected array of protected properties
 * @property-read array $read_only array of read only properties
 * @property-write array $write_only array of write only properties
*/
class SODA
{
/**
 * single instance of the driver class (singleton pattern)
 #
 * @access protected
 #
 * @var object 
 #
 * @static $instance
 #
*/
  protected static $instance;
/**
 * read only properties
 #
 * @access protected
 #
 * @var array 
 #
*/
  protected $read_only=array('_persistant' => '','_driver'=>'','_dbtype'=>'','_connected'=>false,'_select_db'=>false,'_requests'=>array(),'_errors'=>array());
/**
 * write only properties
 #
 * @access protected
 #
 * @var array 
 #
*/
  protected $write_only=array('_persistant' => '','_user'=>'','_pwd'=>'','_server'=>'','_dbname'=>'','_requests'=>array());
/**
 * protected properties
 #
 * @access protected
 #
 * @var array  
 #
*/
  protected $protected=array('_connection' =>'','_lock'=>false);

/**
 * private constructor prevents direct creation of object.
 #
 * 
 * @return true
*/
  private function __construct($dbtype='mysql',$nom='',$pw='',$dbname='',$srv='',$opt=array('_persistant'=>false)) 
    { 
      $classname = 'driver_' . $dbtype;
      //resolves the real dir of the main file
      $rp=realpath($_SERVER['SCRIPT_FILENAME']);
      $txt=file_get_contents($rp);
      if (preg_match("@(?<=[\"'])[^\"']*base\.inc\.php(?=[\"'])@",$txt,$ret))$dirname=dirname($ret[0]);
      else $dirname='.';
      ini_set('include_path',ini_get('include_path').":".realpath($dirname));
      $this->_basedir=$dirname.'/';
      if (file_exists($dirname.'/drivers/' . $classname . '.php') && @include_once $dirname.'/drivers/' . $classname . '.php')
        {
          self::$instance = new $classname($nom,$pw,$dbname,$srv,$opt);
          self::$instance->protected['_basedir']=$dirname.'/';
          self::$instance->protected['_driver_file']=$dirname.'/drivers/' . $classname . '.php';
          return true;
        }
      else
        {
          $drv = $this->find_drivers($dirname);
          if ($drv==false) return false;
          foreach ($drv as $classname=>$file)
            {       
              ini_set('include_path',ini_get('include_path').":".dirname($file));
              if (include_once ($file)) {
                $c= new $classname($nom,$pw,$dbname,$srv,$opt);
                if (@$c->connect())
                  {
                    $c->close();
                    self::$instance = $c;
                    self::$instance->protected['_basedir']=$this->_basedir;
                    self::$instance->protected['_driver_file']=$file;
                    return true;
                  }
              }
            }  
        }
      return false;
    }
  final private function scandir($dir)
    {
      $r=array();
      $dh = opendir($dir);
      while (($file = readdir($dh)) !== false) {
        if ($file!='.' && $file !='..') {
          if (is_dir($dir.'/'.$file)) $r=array_merge($r,$this->scandir($dir.'/'.$file));
          elseif (substr($file,0,7)=='driver_') {
            $nom=substr($file,0,strlen($file)-4);
            $r[$nom]=$dir.'/'.$file;
          }
        }
      }
      return $r;
    }
  final private function find_drivers($dir)
    {
      $dir=$dir.'/drivers';
      $r=false;
      if (is_dir($dir)) 
        {
          if ($dh = opendir($dir)) 
            {
              while (($file = readdir($dh)) !== false) 
                {
                  $d=substr($file,0,7);
                  $nom=substr($file,0,strlen($file)-4);
                  if ($d== 'driver_') $r[$nom]=$dir.'/'.$file;                 
                }
              closedir($dh);
              return $r;
            } else {
              if ($this->_basedir!='./') {
                $this->_basedir='./';
                return $this->find_drivers();
              } else return false;
            }
            
        } else {
          return $this->scandir(realpath('.'));
        }
    }
  protected function init_this() 
    {
      $this->read_only=new Array_Object($this->read_only);
      $this->write_only=new Array_Object($this->write_only);
      $this->protected=new Array_Object($this->protected);
      $this->_requests=new Array_Object(); 
      $this->_errors=new Array_Object(); 
    }
  protected function change_driver($name) 
    {
      $nom=$this->_user;
      $pw=$this->_pwd;
      $dbname=$this->_dbname;
      $srv=$this->_server;
      foreach ($this->read_only as $key=>$value) {
        if (array_key_exists($key,$this->write_only)){
          $opt[$key]=$this->$key;
        }
      }
      self::$instance=false;
      self::create($name,$nom,$pw,$dbname,$srv,$opt);
      foreach($GLOBALS as $key=>$value) {
        if ($value===$this) {
          $nm=$key;
        }
      }
      global $$nm;
      $$nm=self::$instance;
    }
/*-----------------------------------------------------------------------------
 * Method that encrypt properties: 
 * don't forget to modify it before production use for more security
--------------------------------------------------------------------------------*/
  protected function scrypt($sel,$value)
    {
      if (is_scalar($value))
        {
        /*---------------------- modify from here
        ------------------------------------------*/                  
          $pass1="passphrase1";
          $pass2="passphrase2";
          $pass3="passphrase3"; 
        //SSL encryption
        //if the protected variable $this->_certificate is not defined we create a private key and an auto trusted certificate.
        if (!array_key_exists('_certificate',$this->protected))
          {
          $this->protected['_vecteur'] = base64_encode(mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_NOFB), MCRYPT_RAND));
            $dn = array(
              "countryName" => "FR", 
              "stateOrProvinceName" => md5(mt_rand()),
              "localityName" => md5(mt_rand()),
              "organizationName" => md5(mt_rand()),
              "organizationalUnitName" => "SODA_dev",
              "commonName" => md5(mt_rand()),
              "emailAddress" => md5(mt_rand())
             );
            // create a private key
            $privkey = openssl_pkey_new();
            //create the Certificate Signing Request
            $csr = openssl_csr_new($dn, $privkey);
            //trust the request to enable the certificate
	          $sscert = openssl_csr_sign($csr, null, $privkey, 365);
	          //export to string the certificate 
            openssl_x509_export($sscert, $certout);
            //free the certificate ressource
            openssl_x509_free($sscert);
            //export to string the private key
            openssl_pkey_export($privkey, $pkeyout, $pass2);
            //free the key ressource
            openssl_free_key($privkey);
            //we store the certificate and the private key in the protected variables certificate and private_key
            $this->protected['_certificate']=base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $pass3,$certout , MCRYPT_MODE_NOFB,base64_decode($this->protected['_vecteur'])));
            $this->protected['_private_key']=base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $pass1,$pkeyout , MCRYPT_MODE_NOFB,base64_decode($this->protected['_vecteur'])));
          }
        //we get a ressource of a public key
        $certout=mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $pass3, base64_decode($this->protected['_certificate']), MCRYPT_MODE_NOFB,base64_decode($this->protected['_vecteur']));
        $public_key=openssl_pkey_get_public($certout);
        //we encrypt datas with the public key
        openssl_public_encrypt($value, $crypted, $public_key);
        //free the public key ressource
        openssl_free_key($public_key);
        //we encode the saltz to be sure that datas are crypted before decypher.
        $sel=base64_encode($sel);
        //we return saltz and encrypted value as a single string.
        return $sel.base64_encode($crypted); 
        
        /*----------------------------- modify to here
        ----------------------------------------------*/
        }
       //for array, Array_object and stdClass object we decrypt recursively
      elseif (is_array($value) || (is_object($value) && (get_class($value)=='Array_Object'||get_class($value)=='stdClass')))
        {
          $r= new Array_Object($value);
          foreach ($r as $key=>$val)
            {
              $r[$key]=$this->scrypt($key,$val);
            }
          return $r;
        }
      //ressources and complexes objects aren't stored encrypted 
      else return $value;
    }
/*-----------------------------------------------------------------------------
 * Method that decrypt properties: 
 * don't forget to modify it before production use for more security
--------------------------------------------------------------------------------*/
  protected function dcrypt($sel,$value)
    {
      if (is_string($value))
        { 
        /*---------------------- modify from here
        ------------------------------------------*/              
          $pass1="passphrase1";
          $pass2="passphrase2";
        //SSL encryption
        //we get the saltz
        $sel=base64_encode($sel);
        $len=strlen($sel);
        $begin=substr($value,0,$len);
	      //$value is encrypted ?
        if ($begin==$sel)
          {
	        //we extract the value to decypher 
            $v=base64_decode(substr($value,$len,(strlen($value)-$len)));
	        //we get a ressource of the private key
	          $pkeyout=mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $pass1,base64_decode($this->protected['_private_key']) , MCRYPT_MODE_NOFB,base64_decode($this->protected['_vecteur']));
            $private_key=openssl_pkey_get_private($pkeyout,$pass2);
	        //we decypher the variable
            $res= openssl_private_decrypt($v, $dcrypted, $private_key);
	        //free the private key ressource
            openssl_free_key($private_key);
	          //we return the decrypted value
            return $dcrypted;
          }
	      //if the value isn't encrypted we return as it. 
	      else return $value;
	      
        /*----------------------------- modify to here
        ----------------------------------------------*/
	      
        }
      //for array, Array_object and stdClass object we decrypt recursively
      elseif (is_array($value) || (is_object($value) && (get_class($value)=='Array_Object'||get_class($value)=='stdClass')))
        {        
          $r=new Array_Object($value);
          foreach ($r as $key=>$val)
            {
              $r[$key]=$this->dcrypt($key,$val);
            }
          return $r;
        }
      else return $value;
    }
/*---------------------------------------------------------------
 * overloaded functions:
---------------------------------------------------------------*/  
/**
  * override properties setting
  * @category overload
*/
  public function __set($varname,$value)
    {
      if ($this->protected['_lock']===true) {
        $this->read_only['_errors'][]="Can't set variables with locked object";       
        return;
      }
      if (isset($this->_requests[$varname])) {
        $this->_requests[$varname]=$this->scrypt($varname,$value);
        return;
      }     
      $ro=array_key_exists($varname,$this->read_only);
      $wo=array_key_exists($varname,$this->write_only);
      $pr=array_key_exists($varname,$this->protected);
      // who call ?
      $internal=false;
      $bkt=debug_backtrace();
      if(count($bkt)>1 && array_key_exists('object',$bkt[1])) {
        $internal = $bkt[1]['object']===$this;
      }
    //internal call ($this->) 
      if ($internal)
        {
          //if the array key exists in read_only or write_only and not in protected
          if (($ro || $wo) && !$pr)
            {
              $val=$this->scrypt($varname,$value);
              if($ro) $this->read_only[$varname] = $val;
              if($wo) $this->write_only[$varname]= $val;
            }
          //else the variable is protected and not crypted
          else $this->protected[$varname]=$value;
        }
    // external call: for a not defined variable or defined as a read/write or write only variable
      elseif (!$pr && (!$ro || ($ro && $wo)))
        {  
          $val=$this->scrypt($varname,$value);
          $this->write_only[$varname]=$val;
          //if the variable is defined too in read only mode or if it's not defined, it's in read and write mode
          if ($ro || (!$ro && !$wo)) {
            $this->read_only[$varname]=$val;
           }
        }
      else 
        {         
          if ($varname == '_dbtype' && $value!=$this->_dbtype) {
            $this->change_driver($value);
          } 
        }
    }
/**
  * override properties getting
  * @category overload
*/
  public function __get($varname)
    {  
      if($this->protected['_lock']===true){
        $this->read_only['_errors'][]="Can't get variables with locked object";
        return;
      }
      if (isset($this->_requests[$varname])) return $this->_requests[$varname];
      $internal=false;
      $bkt=debug_backtrace();
      if(count($bkt)>1 && array_key_exists('object',$bkt[1])) {
        $internal = $bkt[1]['object']===$this;
      }
      $ro=array_key_exists($varname,$this->read_only);
      if ($ro) {
        $r=$this->dcrypt($varname,$this->read_only[$varname]);
      } 
      elseif ($internal) {
        if (array_key_exists($varname,$this->write_only)){
          $r=$this->dcrypt($varname,$this->write_only[$varname]);
        }
        elseif (array_key_exists($varname,$this->protected)){
          $r=$this->protected[$varname];
        }
      }
      return isset($r)?$r:null;
      
    }
    
  public function __isset($varname)
    {
      $bkt=debug_backtrace();
      $internal=false;
      if(count($bkt)>1 && array_key_exists('object',$bkt[1])) {
        $internal = $bkt[1]['object']===$this;
      }
      if ($internal && array_key_exists($varname,$this->protected))return true; 
      if (array_key_exists($varname,$this->write_only)||array_key_exists($varname,$this->read_only))return true;
      return false;
    }

  public function __unset($varname)
    {
    $bkt=debug_backtrace();
    $internal=false;
    if(count($bkt)>1 && array_key_exists('object',$bkt[1])) {
      $internal = $bkt[1]['object']===$this;
    }
      if ($internal) {
        if (array_key_exists($varname,$this->read_only))unset($this->read_only[$varname]);
        if (array_key_exists($varname,$this->write_only))unset($this->write_only[$varname]);
        if (array_key_exists($varname,$this->protected))unset($this->protected[$varname]);
      } elseif (array_key_exists($varname,$this->write_only))unset($this->write_only[$varname]);
    }
 
/**
 * Prevents users to clone the object 
*/
  protected function __clone()
    {
    }

/**
 * override methods call to permit the use of natives functions of a driver 
 *
 * for example: $this->_connect is the same as mysql_connect if the driver is mysql.
 * <br/>This function use the read only variable $dbtype as a prefix for the called function 
*/
  public function __call($f,$a)
    {
      //for functions wich need a valid ressource
      $this->connect();
      foreach($a as $i=>$v) {
        $arg[]='$a['.$i.']';
      }
      if (isset($arg)&& is_array($arg))$arg=implode($arg,",");
      else $arg='';
      $b=var_export($a,true);
      $f=$this->_dbtype.$f;
      $code='$a='.$b.'; $res='.$f.'('.$arg.');';
      eval($code);
      return isset($res)?$res:false;     
    }
/**
 * Magic method that disconnect to the database before serialization  
 *
*/
  public function __sleep()
    {
      $this->close();
      foreach($this as $key => $value) {
        $r[]=$key;
      }      
      $k=md5(file_get_contents($this->_basedir."/base.inc.php"));
      $this->lock($k);
      return $r;
    }
/**
 * Magic method to reconnect to the database after unserialization  
 *
*/
  public function __wakeup()
    { 
      global $path;     
      $u=$this->unlock(md5(file_get_contents("$path/base.inc.php")));
      $c=$this->connect();
      if(!$c && !$u)$this->read_only['_errors'][]="Can't unlock the object: the file base.inc.php is different from the file wich had constructed the object";
      if(!$c && $u)$this->read_only['_errors'][]="Can't connect to the database because of bad user, password, server or database name";
      
    }

/*--------------- end of overloading
----------------------------------*/
/**
 * Method to lock the object 
 *
*/
  public function lock($key)
    {
      $pvk=$this->protected['_private_key'];
      $this->protected['_private_key']=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key,$pvk , MCRYPT_MODE_NOFB,base64_decode($this->protected['_vecteur']));
      $this->_lock_key=$this->scrypt('_lock_key',$key);
      $this->_lock=true;
      return $this->protected['_lock'];
    }
/**
 * Method to unlock the object 
 *
*/
  public function unlock($key)
    {
      $this->protected['_private_key']=mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key,$this->protected['_private_key'] , MCRYPT_MODE_NOFB,base64_decode($this->protected['_vecteur']));
      $k=$this->dcrypt('_lock_key',$this->protected['_lock_key']);
      if($k==$key){
        $this->protected['_lock']=false;
        unset($this->protected['_lock_key']);
      } else {
        $this->read_only['_errors'][]="Can't unlock this object: bad key given";
        $this->protected['_private_key']=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key,$this->protected['_private_key'], MCRYPT_MODE_NOFB,base64_decode($this->protected['_vecteur']));
      }
      return !$this->protected['_lock'];
    }
/**
 * Method to export the object as string 
 *
*/
  public function export()
    {
      $k=md5(file_get_contents($this->_basedir."/base.inc.php"));
      $s=serialize($this);
      $this->unlock($k);
      return base64_encode($s);
    }
/**
 * Method to export the object and load it in an external file  
 *
*/
  public function export_to_file($filename,$varname='$SODA_obj')
    {
      $inc='ini_get("include_path")';
      $dirname=dirname($filename);
      $bname= basename($filename);
      $df=$this->_driver_file;
      $l=strlen($dirname);
      if (substr($df,0,$l)==$dirname)$df=substr($df,$l,strlen($df)-$l);
      $f='/base.inc.php';
      $e= $this->export();
      $str='<?php
      //You can modify the path value but don\'t mofify it name:  
      $path=realpath("'.$dirname.'");
      ini_set("include_path",'.$inc.'.":$path");
      require_once("$path'.$f.'");
      require_once("$path'.$df.'");
      '.$varname.'= SODA::import("'.$e.'");
?>';
      return file_put_contents($filename,$str);
    }
/**
 * Static Method to import a SODA object 
 *
*/
  public static function import($obj)
    {
      self::$instance= unserialize(base64_decode($obj));
      return self::$instance;
    }
/**
 * Singleton pattern method contructor
 * In order to have a single instance of a class driver the real constructor is private and the construction is made by this static method
 * <br/>If an object is set this function don't create another one but return the existing object. 
*/
  public static function create($dbtype='mysql',$nom='',$pw='',$dbname='',$srv='localhost',$opt=array('_persistant'=>false)) 
    {
      if (!isset(self::$instance) || self::$instance==false) 
        {
          $r=new SODA($dbtype,$nom,$pw,$dbname,$srv,$opt);
          if(!$r) return false;            
        }
      return self::$instance;
    }

}//end of the class SODA

/**
 * This class extends the SPL object array in order to acces at arrays values with a string index like properties of an object. 
 *
 * <b>Example:</b> $mon_tableau=array('cle1'=>'valeur1');
 #
 * You can get the value of the index cle1 with:
 * - or $mon_tableau['cle1']
 * - or $mon_tableau->cle1
 * @package SODA
 * @subpackage SODA
 * @author Salvan Gregory <dev@europeaweb.com>
 * @version 1.1
 * @copyright Copyright (c) 2008, Salvan Gregory europeaweb.com
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
*/
class Array_Object extends ArrayObject
  {
    public function __construct($config = array())
      {
        parent::__construct($config, ArrayObject::ARRAY_AS_PROPS);
      }
/**
 * This method the next integer index
 * As this object extends arrays you can have integers indexes or strings indexes, it's useful to have the next integer index.  
 * <br/> Note: It return the biggest index + 1 example:Array_Object(2=>'valeur') return 3
*/
    public function count_num_index()
      { 
       $i=count($this);
       while (!array_key_exists($i-1,$this) && $i>0)$i--;
       return $i;
      }
//overloading:
    public function __set($varname,$value)
      {
        if (is_array($value))$value=new Array_Object($value);
        $this[$varname]=$value;
      }
    public function __get($varname)
      {
        if(array_key_exists($varname,$this))
          {
            if (is_array($this[$varname]))$this[$varname]= new Array_Object($this[$varname]);
            return $this[$varname];
          }
      }
    final public function __tostring()
      {
        return var_export($this,true);
      }
    public static function _new()
      {
        $numargs = func_num_args();
        $config=array();
        if ($numargs == 1)$config=func_get_arg(0);
        return new Array_Object($config);
      }
  }//end of the class Array_Object

/** 
 * This implements requests objects
 * @package SODA
 * @subpackage request
 * @author Salvan Gregory <dev@europeaweb.com>
 * @version 1.1
 * @copyright Copyright (c) 2008, Salvan Gregory europeaweb.com
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
*/
class request extends Array_Object
  {
    function __construct()
      {
        $prop= array( '_sql'=>'', 
                      '_data'=>false, 
                      '_type'=>'',
                      '_ltype'=>0,
                      '_result'=>false,
                      '_lengths'=>0,
                      '_fields'=>0,
                      '_rows'=>0,
                      '_vars'=>'',
                      '_parent'=>false
                    );
        $prop['_errors']=new Array_Object();
        $prop['_vars']= new Array_Object();
        parent::__construct($prop);
        $bkt=debug_backtrace();
        if (isset($bkt[1]['object'])) $this->_parent =&$bkt[1]['object'];
      }
/**
  *  Parses sql code and replaces variables by its values
  * @return string the correct sql code
 */
    public function get_sql()
      {
        $this->set_sql();
        $ret=$this->_sql;
        foreach ($this->_vars as $key=>$val)
          {
            if (is_int($key)) $ret=preg_replace("@[\?]@m",$val,$ret,1);
            else {
              $search="@\{$key\}@m";
              $ret=preg_replace($search,$val,$ret);
            }
          }
        return $ret;
      }
/**
  *  Analyse sql code to find variables and prepares values of replacement
  *
 */
    public function set_sql()
      {
        $value=$this->_sql;
        preg_match_all( "@\{(?>[a-z_$0-9A-Z]+)\}@xm", $value,$ar);
        if ($ar[0]!=array()) {
          foreach ($ar[0] as $val) {
            $c= count($val) - 2;
            $val=substr($val,1,$c);
            $val=trim($val);
            $$val='';
            if (isset($this->_vars[$val])) $$val=$this->_vars[$val];
            if (isset($this->$val)) {
              $$val=$this->$val;
              unset($this->$val);              
            } else {
              global $$val;
              if (!isset($$val))$$val='';
            }
            $this->_vars[$val]=$$val;
          }
        }
        $c=substr_count($value,'?');
        for ($i=0;$i<$c;$i++){
            $v=isset($this[$i])?$this[$i]:(isset($this->_vars[$i])?$this->_vars[$i]:'');
            unset($this[$i]);
            $this->_vars[$i]=$v;
        }
      }
/**
  *  deletes internal variables to prepare another request
  *
 */
    public function reset()
      {
        $this->_data=false;
        $this->_result=false;
        $this->_lengths=0;
        $this->_fields=0;
        $this->_rows=0;
        $this->_errors= new Array_Object();
      }
/**
  *  overloads methods to permit the call of parent methods as if they were defined for this object 
  *  NOTE: if this object is a property (called 'name') of the object $parent:
  * </br> $parent->name->exec() call $parent->exec('name');
 */
    public function __call($f,$a)
      {       
        if ($this->_parent !=false && isset($this->_parent->_requests))
          {
            //first we find the name of the parent property wich correspond to $this
            foreach($this->_parent->_requests as $name=>$prop) {
              if ($prop=== $this) $myname = $name;
            }
            //then we call the parent method by passing the name of $this as a parameter.
            if (isset($myname)){
              if ($f=='exec'|| $f=='clean'){
                $args=array_merge(array($myname),$a);
                return call_user_func_array(array($this->_parent, $f),$args);
              }
            }
          }
        return;
      }
  }//end of the class request 



















?>