31 import(
'Dataface/Application.php');
32 import(
'Dataface/Table.php');
56 $db = DATAFACE_DB_HANDLE;
61 if ( @$this->app->_conf[
'cache_queries'] and !$this->_fcache_base_path ){
62 if ( is_writable(DATAFACE_SITE_PATH.
'/templates_c') ){
63 $this->_fcache_base_path = DATAFACE_SITE_PATH.
'/templates_c/query_results';
65 $this->_fcache_base_path = DATAFACE_PATH.
'/Dataface/templates_c/query_results';
68 if ( !file_exists($this->_fcache_base_path) ){
70 mkdir($this->_fcache_base_path, 0700);
71 file_put_contents($this->_fcache_base_path.
'/.htaccess',
'deny from all');
87 if ( !isset($this->_cache) ){
88 $filepath = DATAFACE_CACHE_PATH.
'/Dataface_DB.cache';
91 if ( is_readable($filepath) and filemtime($filepath) > time()-1 ){
93 include DATAFACE_CACHE_PATH.
'/Dataface_DB.cache';
96 if ( isset( $cache ) ){
97 $this->_cache =& $cache;
99 $this->_cache = array();
101 register_shutdown_function(array(&$this,
'writeCache'));
109 function cache($key, $value, $lang=null){
110 if ( !isset($lang) ) $lang = $this->_app->_conf[
'lang'];
113 $this->_cache[$lang][$key] = $value;
114 $this->_cacheDirtyFlag =
true;
124 if ( !isset($this->_parser)){
125 import(
'SQL/Parser.php');
126 $this->_parser =
new SQL_Parser(null,
'MySQL');
135 if ( !isset($this->_compiler) ){
136 import(
'SQL/Compiler.php');
137 $this->_compiler = SQL_Compiler::newInstance(
'mysql');
154 $len = strlen($query);
160 $output_args = array();
162 for ($i=0;$i<$len;$i++){
165 case '\\': $escaped = !$escaped;
167 case '"' :
if ( !$escaped && !$sglquoted ){
168 $dblquoted = !$dblquoted;
172 $output_args[] = $buffer;
174 $output_query .=
'"_'.$count.
'_"';
180 case '\'' :
if (!$escaped && !$dblquoted) {
181 $sglquoted = !$sglquoted;
185 $output_args[] = $buffer;
187 $output_query .=
'\'_
'.$count.'_\
'';
197 if ( $query{$i} !=
'\\' ) $escaped =
false;
198 if ( $skip )
continue;
199 if ( $dblquoted || $sglquoted) {
200 $buffer .= $query{$i};
202 else $output_query .= $query{$i};
206 $this->_matchCount = 0;
207 $this->_matches = array();
208 $output_query = preg_replace_callback(
'/\b(-{0,1})([0-9]*\.{0,1}[0-9]+)\b/', array(&$this,
'_replacePrepareDigits'), $output_query);
209 $output_args = array($output_query, $output_args, $this->_matches);
218 $this->_matches[] = $matches[1].$matches[2];
223 return $matches[1].$this->_matches[intval($matches[2])-1].$matches[3];
227 return $this->_matches[intval($matches[1])-1];
233 if (
PEAR::isError($blob) )
throw new Exception($blob->toString(), E_USER_ERROR);
235 return mysql_real_escape_string(file_get_contents($blob));
241 $numArgs = count($prepared_query[1]);
242 $buffer = $prepared_query[0];
243 $this->_matches = $prepared_query[2];
244 $buffer = preg_replace_callback(
'/\b([0-9]+)\b/', array(&$this,
'_replaceCompileDigits'), $buffer);
246 $this->_matches = $prepared_query[1];
247 $buffer = preg_replace_callback(
'/([\'"])_(\d+)_([\'"])/', array(&$this,
'_replaceCompileStrings'), $buffer);
249 $buffer = preg_replace_callback(
'/-=-=B(\d+)=-=-/', array(&$this,
'_replaceBlobs'), $buffer);
266 if ( $lang === null ){
268 $lang = $this->app->_conf[
'lang'];
272 if ( strtolower(substr($query, 0, strlen(
'DELETE '))) ==
'delete ' ){
276 $original_query = $query;
278 if ( isset( $this->_cache[$lang][$prepared_query[0]] )){
282 $prepared_query[0] = $this->_cache[$lang][$prepared_query[0]];
286 $query = $prepared_query[0];
287 import(
'Dataface/QueryTranslator.php');
289 $output = $translator->translateQuery($prepared_query[0]);
292 throw new Exception(df_translate(
'scripts.Dataface.DB.translate_query.FAILED_TO_TRANSLATE',
"Failed to translate query: $query.: ",array(
'query'=>$query)).$output->toString(), E_USER_ERROR);
295 $this->
cache($prepared_query[0], $output, $lang);
296 $prepared_query[0] = $output;
307 function query($sql, $db=null, $lang=null, $as_array=
false, $enumerated=
false){
311 $refreshModTimes=
false;
312 if ( $as_array and ($isSelect = (strpos(strtolower(trim($sql)),
'select ') === 0)) ){
313 if ( ($results = $this->
memcache_get($sql, $lang)) or is_array($results) ) {
314 if ( @$this->app->_conf[
'cache_queries_log']){
315 $fp = fopen(
'/tmp/querylog.log',
'a');
316 fwrite(
$fp,
"\n[".date(
'Y-m-d H:i:s').
"] Cached: ".$sql);
322 if ( @$this->app->_conf[
'cache_queries_log']){
323 $fp = fopen(
'/tmp/querylog.log',
'a');
324 fwrite(
$fp,
"\n[".date(
'Y-m-d H:i:s').
"] Failed cached: ".$sql);
327 $this->cache_fails++;
331 }
else if ( @
$app->_conf[
'cache_queries'] ){
332 $refreshModTimes =
true;
341 if ( ( $this->app->_conf[
'multilingual_content'])) {
342 if ( @
$app->_conf[
'debug_sql'] ){
343 error_log(
"Before translation: ".$sql);
347 if ( @
$app->_conf[
'debug_sql'] ){
348 if ( is_array($sql) ){
349 foreach ($sql as $sqli){
350 error_log(
"After translation: ".$sqli);
353 error_log(
"After translation: ".$sql);
361 $db = $this->app->db();
363 $update_insert_id =
true;
364 if ( is_array($sql) ){
367 foreach ($sql as $q){
368 if ( $loopctr++ > 0 and mysql_insert_id($db) ){
369 $this->_insert_id = mysql_insert_id($db);
370 $update_insert_id =
false;
371 $q = str_replace(
"'%%%%%__MYSQL_INSERT_ID__%%%%%'", mysql_insert_id($db), $q );
373 if ( defined(
'DATAFACE_DEBUG_DB') or @
$app->_conf[
'debug_sql']) echo
"Performing query: '$q' <br>";
374 $res = mysql_query($q, $db);
378 if ( defined(
'DATAFACE_DEBUG_DB') ) echo
"Performing query: '$sql' <br>";
380 $res = mysql_query($sql, $db);
383 if ( $update_insert_id ) $this->_insert_id = mysql_insert_id($db);
385 if ( $as_array and $isSelect ){
392 while ( $row = ($enumerated ? mysql_fetch_row($res) : mysql_fetch_assoc($res)) ){
397 @mysql_free_result($res);
412 static $instance = null;
413 if ( $instance === null ){
429 if ( $this->_cacheDirtyFlag ){
436 foreach ($this->_cache as $lang=>$values){
437 foreach ($values as $key=>$value){
438 if ( is_array($value) ){
439 foreach ($value as $innerValue){
440 echo
'$cache[\''.$lang.
'\'][\
''.str_replace(
"'",
"\\'", $key).
'\'][] = \
''.str_replace(
"'",
"\\'", $innerValue).
'\';
444 echo '$cache[\
''.$lang.
'\'][\
''.str_replace(
"'",
"\\'", $key).
'\'] = \
''.str_replace(
"'",
"\\'", $value).
'\';
452 $contents = ob_get_contents();
454 if ( !file_exists(DATAFACE_CACHE_PATH) ) @mkdir(DATAFACE_CACHE_PATH);
456 if ( !$fh or !fwrite($fh, $contents) ){
457 error_log("Failed to write DB cache", E_USER_ERROR);
464 function registerBlob($blobData){
466 $this->blobs[$id++] = $blobData;
472 function checkoutBlob($blobID){
473 if ( !isset( $this->blobs[$blobID]) ) return PEAR::raiseError(df_translate('scripts.Dataface.DB.checkoutBlob.BLOB_DOESNT_EXIST
', "Blob with ID $blobID doesn't exist.
",array('blobID'=>$blobID)), DATAFACE_E_ERROR);
475 $blob = $this->blobs[$blobID];
476 unset($this->blobs[$blobID]);
480 function startTransaction(){ return mysql_query('begin', df_db() ); }
481 function commitTransaction(){ return mysql_query('commit', df_db() ); }
482 function rollbackTransaction(){ return mysql_query('rollback', df_db() ); }
484 function memcache_get($sql, $lang=null){
486 $app =& Dataface_Application::getInstance();
488 $memcache =& $app->memcache;
490 if ( !@$app->_conf['cache_queries'] ) {
495 $key = $this->memcache_get_key($sql, $lang);
499 $tables = $this->getTableDependencies($sql, $lang);
500 if ( PEAR::isError($tables) ) return null;
501 // This is a list of the tables that would cause the cache to be invalidated.
503 $modification_times = Dataface_Table::getTableModificationTimes();
505 foreach ( $tables as $table){
506 if ( isset($modification_times[$table]) ) $mtime = max($mtime, $modification_times[$table]);
508 $t =& Dataface_Table::loadTable($table);
509 if ( @$t->_atts['__source_tables__'] ){
510 $ts = explode(',', $t->_atts['__source_tables__']);
511 foreach ($ts as $tst){
512 if ( isset($modification_times[trim($tst)]) ){
513 $mtime = max($mtime, $modification_times[trim($tst)]);
520 //echo "$table no modified date
";
530 // Now we will get the cached value if it is newer than $mtime
532 if ( $memcache ) $cache_mtime = $this->memcache_mtime($key);
533 else $cache_mtime = $this->fcache_mtime($key);
534 //echo "Cache time
for ".$this->fcache_path($key)." is $cache_mtime
";
535 //echo "[$sql : $cache_mtime : $mtime]
";
536 if ( $cache_mtime > $mtime ){
539 if ( $result = $memcache->get($key) ) {
540 return unserialize($result);
543 else if (($result = $this->fcache_get($key)) !== null ){
545 return unserialize($result);
557 function memcache_mtime($key, $set=false){
559 $key .= '&-action=mtime';
561 if ( DATAFACE_EXTENSION_LOADED_APC and !$set ){
562 return apc_fetch($key);
563 } else if ( DATAFACE_EXTENSION_LOADED_APC and $set ){
564 apc_store($key, time());
567 $_SESSION[$key] = time();
568 } else if ( !$set and isset($_SESSION[$key])){
570 return $_SESSION[$key];
577 function fcache_mtime($key){
578 if ( file_exists($this->fcache_path($key)) ){
579 //echo "Checking mtime
for $key :
".$this->fcache_path($key);
580 return filemtime($this->fcache_path($key));
582 //echo "File does not exist :
".$this->fcache_path($key);
587 function fcache_get($key){
589 if ( file_exists($this->fcache_path($key)) ){
590 return file_get_contents($this->fcache_path($key));
595 function fcache_set($key, $value){
597 file_put_contents($this->fcache_path($key), serialize($value));
600 function fcache_path($key){
601 return $this->_fcache_base_path.'/'.md5($key);
604 function memcache_get_key($sql, $lang){
606 $app =& Dataface_Application::getInstance();
608 $auth =& Dataface_AuthenticationTool::getInstance();
611 $dbname = $app->_conf['_database']['name'];
613 if ( !isset($lang) ) $lang = $app->_conf['lang'];
615 $key = urlencode($dbname).'?-query='.urlencode($sql).'&-lang='.urlencode($lang);
620 function memcache_set($sql, $lang, $value){
622 $app =& Dataface_Application::getInstance();
623 $memcache =& $app->memcache;
624 if ( !$memcache and !@$app->_conf['cache_queries'] ) return null;
626 $key = $this->memcache_get_key($sql, $lang);
628 $memcache->set($key, serialize($value), false, 0);
629 $this->memcache_mtime($key, true);
630 } else if ( @$app->_conf['cache_queries'] ){
631 //echo "Setting $sql $key
".$this->fcache_path($key);
632 $this->fcache_set($key, $value);
638 function getTableDependencies($sql, $lang=null){
639 $app =& Dataface_Application::getInstance();
640 $key = $this->memcache_get_key($sql, $lang);
641 $key .= '&-action=deps';
643 if ( DATAFACE_EXTENSION_LOADED_APC && !isset($_GET['--clear-cache']) ){
644 $deps = apc_fetch($key);
645 if ( is_array($deps) ) return $deps;
646 } else if ( isset($_SESSION[$key]) && !isset($_GET['--clear-cache']) ){
647 $deps = $_SESSION[$key];
648 if ( is_array($deps) ) return $deps;
650 // We actually need to calculate the dependencies, so we will
651 // parse the SQL query.
652 import('SQL/Parser.php');
653 $parser = new SQL_Parser( null, 'MySQL');
654 $data =& $parser->parse($sql);
655 if ( PEAR::isError($data) ){
658 $tables = array_unique($data['all_tables']);
660 if ( @$app->_conf['cache_queries_log'] ){
661 $fp = fopen('/tmp/querylog.log', 'a');
662 fwrite($fp, "\n[
".date('Y-m-d H:i:s')."] Dependencies:
".implode(',', $tables)." ".$sql);
665 //import('SQL/Parser/wrapper.php');
668 //$wrapper = new SQL_Parser_wrapper($data);
669 //$tables = $wrapper->getTableNames();
671 foreach ($tables as $tid=>$table){
672 if ( preg_match('/^dataface__view_(.*)_[a-z0-9]{32}$/', $table, $matches) ){
673 $tables[$tid] = $table = $matches[1];
675 $tobj =& Dataface_Table::loadTable($table,null,true);
676 if ( is_a($tobj, 'Dataface_Table') and isset($tobj->_atts['__dependencies__']) ){
677 $deps = array_map('trim', explode(',', $tobj->_atts['__dependencies__']));
678 $tables = array_merge($tables, $deps);
683 if ( isset($app->_conf['__dependencies__']) ){
684 $deps = array_map('trim',explode(',', $app->_conf['__dependencies']));
685 $tables = array_merge($tables, $deps);
688 $deps = array_unique($tables);
690 if ( DATAFACE_EXTENSION_LOADED_APC ){
691 apc_store($key, $deps);
693 $_SESSION[$key] = $deps;