PHP 用のカスタム SESSION クラスを作成して、セッションをデータベースに移動し、ファイル システムから移動しようとしています。_read()
クロージャー$connect2DB
が呼び出されているときに、メソッドでのみエラーがスローされます。他のすべてのメソッドでは、呼び出し$connect2DB
によってエラーは発生しません。また、クラスを自分でオブジェクトとしてインスタンス化すると、悪名高いものを含むすべてのメソッド_read()
が期待どおりに機能します。エラーを見つけることができますか?
私は次のコードを持っています:
class sessionDBHandler {
public function __construct() {
session_set_save_handler(
array($this, "_open"),
array($this, "_close"),
array($this, "_read"),
array($this, "_write"),
array($this, "_destroy"),
array($this, "_gc")
);
register_shutdown_function('session_write_close');
}
public function _open() {
global $sessCon;
global $HOST;
global $USER;
global $PW;
global $DATABASE;
$sessCon=new mysqli($HOST,$USER,$PW,$DATABASE); //mysqli connection
}
public function _close() {
global $sessCon;
$sessCon->close();
}
public function _read($id) {
global $sessCon;
global $connect2DB;
global $SESSIONTAB;
$qRes=$connect2DB($sessCon,'select `data` from `'.$SESSIONTAB.'` where `ID`=?',array("s",$id)); //<-- error message here
if(!empty($qRes["res"][0]["data"])) return $qRes["res"][0]["data"];
else return '';
}
public function _write($id,$data) {
global $sessCon;
global $connect2DB;
global $SESSIONTAB;
$qRes=$connect2DB($sessCon,'select `ID` from `'.$SESSIONTAB.'` where `ID`=?',array("s",$id));
$param=array(); //buliding SQL query parameters
$param[0]="sis";
$param[]=$id;
$param[]=time();
$param[]=$data;
if(!empty($qRes["res"][0]["ID"])) {
$param[0].="s";
$param[]=$id;
}
$qRes=$connect2DB($sessCon,(!empty($qRes["res"][0]["ID"]) ? 'update' : 'insert').' `'.$SESSIONTAB.'` set `ID`=?, `access`=?, `data`=?'.(!empty($qRes["res"][0]["ID"]) ? ' where `ID`=?' : ''),$param);
if($qRes["info"]["affectedRows"]>0) return true;
else return '';
}
public function _destroy($id) {
global $sessCon;
global $connect2DB;
global $SESSIONTAB;
$qRes=$connect2DB($sessCon,'delete from `'.$SESSIONTAB.'` where `ID`=?',array("s",$id));
if($qRes["info"]["affectedRows"]>0) return true;
else return '';
}
public function _gc($max) {
global $sessCon;
global $connect2DB;
global $SESSIONTAB;
$qRes=$connect2DB($sessCon,'delete from `'.$SESSIONTAB.'` where `access`<?',array("i",(time()-$max)));
if($qRes["info"]["affectedRows"]>0) return true;
else return '';
}
}
そして、これらの関数を見つける場所を PHP に伝えます (session_start()
ここで呼び出す前に:
new sessionDBHandler();
私の問題は、MySQL 抽象化レイヤーとして使用しているクロージャーにあるようです。私が得るエラーは次のとおりです。
Fatal error: Function name must be a string in /Users/user/Sites/script.php on line 401
これは、どういうわけか $connect2DB の名前空間が見つからないことを示唆しています。ただし、__constructor とそのsession_set_save_handler
ディレクティブを使用せずにこれらの関数を呼び出すと、問題なく動作します。
$test=new sessionDBHandler();
$test->_open();
echo $test->_read("d6e08112ff6fc8a7eb6e76832327bf81");
$test->_close();
上記はエラーなしで動作します。ここでクロージャーを使用しているのは、このスクリプトのフットプリントを非常に低く保ち、スクリプトの下部にある $connect2DB を設定解除する必要があるためです。そのため、現在の抽象化レイヤーを維持したいと考えています。
私がここでできることはありますか?
みんな、ありがとう!
おそらく、セッションが呼び出されているときに $connect2DB が初期化されていませんか?
connect2DB
/**
* Simple MySQLi abstraction layer
*
* @param resource $mysqli The MySQLi connection link
* @param string $query The MySQL query for prepaired statement
* @param array $v The parameters to replace ? in $query. First element must be the type
* @param integer $o Option for more debug infos [0]=no infos(default) [1]=adding debug infos
*
* @return array [for select]=associative array of table result [for everything else]=associative array with affectedRows,info and insertID
*
* @author Dominik Wilkowski
*/
$connect2DB=function($mysqli,$query,$v=array(),$o=0) {
if($mysqli->connect_errno) {
return array('info'=>array('error'=>'Connect failed: '.$mysqli->connect_error)); //error handling here
exit();
}
if($v && (substr_count($query,"?")!=strlen($v[0]) || strlen($v[0])!=((count($v)-1)>=0 ? (count($v)-1) : 0))) {
return array('info'=>array('error'=>'Placeholders are unequal! placeholders:'.substr_count($query,"?").', replacements:'.strlen($v[0]).', param:'.(count($v)-1).' ('.$v[0].')')); //error handling here...
exit();
}
if($res=$mysqli->prepare($query)) {
//dynamically bind all $v
if($v) {
$values=array($v[0]);
for($i=1; $i<count($v); $i++) {
${'bind'.$i}=$v[$i];
$values[]=&${'bind'.$i};
}
call_user_func_array(array($res,'bind_param'),$values);
}
$res->execute();
//bind all table rows to result
if(strtolower(substr($query,0,6))=="select") {
$field=$fields=$tempRow=array();
$meta=$res->result_metadata();
while($field=$meta->fetch_field()) {
$fieldName=$field->name;
$fields[]=&$tempRow[$fieldName];
}
$meta->free_result();
call_user_func_array(array($res,"bind_result"),$fields);
//return associative array
$results=array();
$i=0;
while($res->fetch()) {
$results["res"][$i]=array();
foreach($tempRow as $k=>$v2) $results["res"][$i][$k] = $v2;
$i++;
}
$res->free_result();
}
else { //return infos about the query
if($mysqli->warning_count) {
if($err=$mysqli->query("SHOW WARNINGS")) {
$row=$err->fetch_row();
$results["info"]["error"].=$row[0].' ('.$row[1].'): '.$row[2];
$err->close();
}
}
$results["info"]["affectedRows"]=$mysqli->affected_rows;
$results["info"]["info"]=$mysqli->info;
$results["info"]["insertID"]=$mysqli->insert_id;
}
$res->close();
}
if($o===1) { //adding debug infos
$q=$query;
for($i=1;$i<count($v);$i++) $q=preg_replace("/\?/",(substr($v[0],($i-1),1)=="s" ? '"' : '').$v[$i].(substr($v[0],($i-1),1)=="s" ? '"' : ''),$q,1);
$results["info"]["query"]=$q;
$results["info"]["param"]=json_encode($v);
}
if(strtolower(substr($query,0,6))=="update" || strtolower(substr($query,0,6))=="delete") { //optimize at update and delete
preg_match_all('/((update|delete) `(.*)` )/i',$query,$tables);
foreach($tables[3] as $t) $mysqli->query('OPTIMIZE TABLE '.$t);
}
return $results;
};
アップデート #2
以前のテストでは、実行時に名前空間が実際に初期化されていないように見えることが示唆された_read()
ため、これを再確認するための小さなテスト ケースを作成しました。
<?php
$LOGIN='login credentials';
//Closure
$nob=function() {
return 'closure running!<br>';
};
//Session handler
class sessionDBHandler {
public function __construct() {
session_set_save_handler(
array($this, "_open"),
array($this, "_close"),
array($this, "_read"),
array($this, "_write"),
array($this, "_destroy"),
array($this, "_gc")
);
register_shutdown_function('session_write_close');
}
public function _open() {
global $sessCon;
global $LOGIN;
echo '_open running with: '.$LOGIN.'<br>';
$sessCon='connection established!<br>';
}
public function _close() {
global $sessCon;
echo $sessCon='connection closed!<br>';
}
public function _read($id) {
global $sessCon;
global $nob;
echo $nob();
echo 'requested id is: '.$id.'<br>';
echo 'sessCon is: '.$sessCon;
}
public function _write($id,$data) {
global $sessCon;
echo 'requested id is: '.$id.' and data: '.$data.'<br>';
echo 'sessCon is: '.$sessCon;
}
public function _destroy($id) {
global $sessCon;
echo 'requested id is: '.$id.'<br>';
echo 'sessCon is: '.$sessCon;
}
public function _gc($max) {
global $sessCon;
echo 'requested max is: '.$max.'<br>';
echo 'sessCon is: '.$sessCon;
}
}
new sessionDBHandler();
session_start();
?>
興味深いことに、これはエラーなしで実行され、次のように出力されます。
_open running with: login credentials
closure running!
requested id is: 746b70a979d9ef8c6686e659707b4b38
sessCon is: connection established!
requested id is: 746b70a979d9ef8c6686e659707b4b38 and data:
sessCon is: connection established!
connection closed!
これは、私の特定の実装にエラーがあるように思われることを示唆しています。これに挑戦したい人は誰でも大歓迎です:)
アップデート #3
アプリケーションを実行した後にクロージャーを設定解除したいのでunset($connect2DB);
、スクリプトの一番下にありました。ただし、これはどういうわけか「時期尚早」の設定解除を引き起こしたようです。これを削除すると、エラーが削除されます。
でも
スクリプトの実行後にクロージャを安全に設定解除して、メイン スクリプトに流れ込まないようにする方法はありますか?