<?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
?>
|