bnbweb/includes/cls_sql_executor.php
2023-06-28 04:27:57 +08:00

899 lines
29 KiB
PHP
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
if (!defined('IN_ECS'))
{
die('Hacking attempt');
}
class sql_executor
{
/**
* 记录程序执行过程中最后产生的那条错误信息
*
* @access public
* @var string $error
*/
var $error = '';
/**
* 存储将被忽略的错误号,这些错误不会记录在$error属性中
* 但仍然会记录在错误日志文件当中。
*
* @access private
* @var array $ignored_errors
*/
var $ignored_errors = array();
/**
* MySQL对象
*
* @access private
* @var object $db
*/
var $db = '';
/**
* 数据库字符编码
*
* @access private
* @var string $charset
*/
var $db_charset = '';
/**
* 替换前表前缀
*
* @access private
* @var string $source_prefix
*/
var $source_prefix = '';
/**
* 替换后表前缀
*
* @access private
* @var string $target_prefix
*/
var $target_prefix = '';
/**
* 当发生错误时,程序将把日志记录在该指定的文件中
*
* @access private
* @var string $log_path
*/
var $log_path = '';
/**
* 开启此选项后,程序将进行智能化地查询操作,即使重复运行本程序,也不会引起数据库的查询冲突。这点在浏览器
* 和服务器之间进行通讯时是非常有必要的,因为网络很有可能在您不经意间发生中断。不过,由于用到了大量的正则
* 表达式,开启该选项后将非常耗费服务器的资源。
*
* @access private
* @var boolean $auto_match
*/
var $auto_match = false;
/**
* 记录当前正在执行的SQL文件名
*
* @access private
* @var string $current_file
*/
var $current_file = 'Not a file, but a string.';
/**
* 构造函数
*
* @access public
* @param mysql $db mysql类对象
* @param string $charset 字符集
* @param string $sprefix 替换前表前缀
* @param string $tprefix 替换后表前缀
* @param string $log_path 日志路径
* @param boolean $auto_match 是否进行智能化查询
* @param array $ignored_errors 忽略的错误号数组
* @return void
*/
function __construct($db, $charset = 'gbk', $sprefix = 'ecs_', $tprefix = 'ecs_', $log_path = '', $auto_match = false, $ignored_errors = array())
{
$this->sql_executor($db, $charset, $sprefix, $tprefix, $log_path, $auto_match, $ignored_errors);
}
/**
* 构造函数
*
* @access public
* @param mysql $db mysql类对象
* @param string $charset 字符集
* @param string $sprefix 替换前表前缀
* @param string $tprefix 替换后表前缀
* @param string $log_path 日志路径
* @param boolean $auto_match 是否进行智能化查询
* @param array $ignored_errors 忽略的错误号数组
* @return void
*/
function sql_executor($db, $charset = 'gbk', $sprefix = 'ecs_', $tprefix = 'ecs_', $log_path = '', $auto_match = false, $ignored_errors = array())
{
$this->db = $db;
$this->db_charset = $charset;
$this->source_prefix = $sprefix;
$this->target_prefix = $tprefix;
$this->log_path = $log_path;
$this->auto_match = $auto_match;
$this->ignored_errors = $ignored_errors;
}
/**
* 执行所有SQL文件中所有的SQL语句
*
* @access public
* @param array $sql_files 文件绝对路径组成的一维数组
* @return boolean 执行成功返回true失败返回false。
*/
function run_all($sql_files)
{
/* 如果传入参数不是数组,程序直接返回 */
if (!is_array($sql_files))
{
return false;
}
foreach ($sql_files AS $sql_file)
{
$query_items = $this->parse_sql_file($sql_file);
/* 如果解析失败,则跳过 */
if (!$query_items)
{
continue;
}
foreach ($query_items AS $query_item)
{
/* 如果查询项为空,则跳过 */
if (!$query_item)
{
continue;
}
if (!$this->query($query_item))
{
return false;
}
}
}
return true;
}
/**
* 获得分散的查询项
*
* @access public
* @param string $file_path 文件的绝对路径
* @return mixed 解析成功返回分散的查询项数组失败返回false。
*/
function parse_sql_file($file_path)
{
/* 如果SQL文件不存在则返回false */
if (!file_exists($file_path))
{
return false;
}
/* 记录当前正在运行的SQL文件 */
$this->current_file = $file_path;
/* 读取SQL文件 */
$sql = implode('', file($file_path));
/* 删除SQL注释由于执行的是replace操作所以不需要进行检测。下同。 */
$sql = $this->remove_comment($sql);
/* 删除SQL串首尾的空白符 */
$sql = trim($sql);
/* 如果SQL文件中没有查询语句则返回false */
if (!$sql)
{
return false;
}
/* 替换表前缀 */
$sql = $this->replace_prefix($sql);
/* 解析查询项 */
$sql = str_replace("\r", '', $sql);
$query_items = explode(";\n", $sql);
return $query_items;
}
/**
* 执行某一个查询项
*
* @access public
* @param string $query_item 查询项
* @return boolean 成功返回true失败返回false。
*/
function query($query_item)
{
/* 删除查询项首尾的空白符 */
$query_item = trim($query_item);
/* 如果查询项为空则返回false */
if (!$query_item)
{
return false;
}
/* 处理建表操作 */
if (preg_match('/^\s*CREATE\s+TABLE\s*/i', $query_item))
{
if (!$this->create_table($query_item))
{
return false;
}
}
/* 处理ALTER TABLE语句此时程序将对表的结构进行修改 */
elseif ($this->auto_match && preg_match('/^\s*ALTER\s+TABLE\s*/i', $query_item))
{
if (!$this->alter_table($query_item))
{
return false;
}
}
/* 处理其它修改操作,如数据添加、更新、删除等 */
else
{
if (!$this->do_other($query_item))
{
return false;
}
}
return true;
}
/**
* 过滤SQL查询串中的注释。该方法只过滤SQL文件中独占一行或一块的那些注释。
*
* @access public
* @param string $sql SQL查询串
* @return string 返回已过滤掉注释的SQL查询串。
*/
function remove_comment($sql)
{
/* 删除SQL行注释行注释不匹配换行符 */
$sql = preg_replace('/^\s*(?:--|#).*/m', '', $sql);
/* 删除SQL块注释匹配换行符且为非贪婪匹配 */
//$sql = preg_replace('/^\s*\/\*(?:.|\n)*\*\//m', '', $sql);
$sql = preg_replace('/^\s*\/\*.*?\*\//ms', '', $sql);
return $sql;
}
/**
* 替换查询串中数据表的前缀。该方法只对下列查询有效CREATE TABLE,
* DROP TABLE, ALTER TABLE, UPDATE, REPLACE INTO, INSERT INTO
*
* @access public
* @param string $sql SQL查询串
* @return string 返回已替换掉前缀的SQL查询串。
*/
function replace_prefix($sql)
{
$keywords = 'CREATE\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?|'
. 'DROP\s+TABLE(?:\s+IF\s+EXISTS)?|'
. 'ALTER\s+TABLE|'
. 'UPDATE|'
. 'REPLACE\s+INTO|'
. 'DELETE\s+FROM|'
. 'INSERT\s+INTO';
$pattern = '/(' . $keywords . ')(\s*)`?' . $this->source_prefix . '(\w+)`?(\s*)/i';
$replacement = '\1\2`' . $this->target_prefix . '\3`\4';
$sql = preg_replace($pattern, $replacement, $sql);
$pattern = '/(UPDATE.*?WHERE)(\s*)`?' . $this->source_prefix . '(\w+)`?(\s*\.)/i';
$replacement = '\1\2`' . $this->target_prefix . '\3`\4';
$sql = preg_replace($pattern, $replacement, $sql);
return $sql;
}
/**
* 获取表的名字。该方法只对下列查询有效CREATE TABLE,
* DROP TABLE, ALTER TABLE, UPDATE, REPLACE INTO, INSERT INTO
*
* @access public
* @param string $query_item SQL查询项
* @param string $query_type 查询类型
* @return mixed 成功返回表的名字失败返回false。
*/
function get_table_name($query_item, $query_type = '')
{
$pattern = '';
$matches = array();
$table_name = '';
/* 如果没指定$query_type则自动获取 */
if (!$query_type && preg_match('/^\s*(\w+)/', $query_item, $matches))
{
$query_type = $matches[1];
}
/* 获取相应的正则表达式 */
$query_type = strtoupper($query_type);
switch ($query_type)
{
case 'ALTER' :
$pattern = '/^\s*ALTER\s+TABLE\s*`?(\w+)/i';
break;
case 'CREATE' :
$pattern = '/^\s*CREATE\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?\s*`?(\w+)/i';
break;
case 'DROP' :
$pattern = '/^\s*DROP\s+TABLE(?:\s+IF\s+EXISTS)?\s*`?(\w+)/i';
break;
case 'INSERT' :
$pattern = '/^\s*INSERT\s+INTO\s*`?(\w+)/i';
break;
case 'REPLACE' :
$pattern = '/^\s*REPLACE\s+INTO\s*`?(\w+)/i';
break;
case 'UPDATE' :
$pattern = '/^\s*UPDATE\s*`?(\w+)/i';
break;
default :
return false;
}
if (!preg_match($pattern, $query_item, $matches))
{
return false;
}
$table_name = $matches[1];
return $table_name;
}
/**
* 获得SQL文件中指定的查询项
*
* @access public
* @param string $file_path SQL查询项
* @param int $pos 查询项的索引号
* @return mixed 成功返回该查询项失败返回false。
*/
function get_spec_query_item($file_path, $pos)
{
$query_items = $this->parse_sql_file($file_path);
if (empty($query_items)
|| empty($query_items[$pos]))
{
return false;
}
return $query_items[$pos];
}
/**
* 概据MYSQL版本创建数据表
*
* @access public
* @param string $query_item SQL查询项
* @return boolean 成功返回true失败返回false。
*/
function create_table($query_item)
{
/* 获取建表主体串以及表属性声明串,不区分大小写,匹配换行符,且为贪婪匹配 */
$pattern = '/^\s*(CREATE\s+TABLE[^(]+\(.*\))(.*)$/is';
if (!preg_match($pattern, $query_item, $matches))
{
return false;
}
$main = $matches[1];
$postfix = $matches[2];
/* 从表属性声明串中查找表的类型 */
$pattern = '/.*(?:ENGINE|TYPE)\s*=\s*([a-z]+).*$/is';
$type = preg_match($pattern, $postfix, $matches) ? $matches[1] : 'MYISAM';
/* 从表属性声明串中查找自增语句 */
$pattern = '/.*(AUTO_INCREMENT\s*=\s*\d+).*$/is';
$auto_incr = preg_match($pattern, $postfix, $matches) ? $matches[1] : '';
/* 重新设置表属性声明串 */
$postfix = $this->db->version() > '4.1' ? " ENGINE=$type DEFAULT CHARACTER SET " . $this->db_charset
: " TYPE=$type";
$postfix .= ' ' . $auto_incr;
/* 重新构造建表语句 */
$sql = $main . $postfix;
/* 开始创建表 */
if (!$this->db->query($sql, 'SILENT'))
{
$this->handle_error($sql);
return false;
}
return true;
}
/**
* 修改数据表的方法。算法设计思路:
* 1. 先进行字段修改操作。CHANGE
* 2. 然后进行字段移除操作。DROP [COLUMN]
* 3. 接着进行字段添加操作。ADD [COLUMN]
* 4. 进行索引移除操作。DROP INDEX
* 5. 进行索引添加操作。ADD INDEX
* 6. 最后进行其它操作。
*
* @access public
* @param string $query_item SQL查询项
* @return boolean 修改成功返回true否则返回false
*/
function alter_table($query_item)
{
/* 获取表名 */
$table_name = $this->get_table_name($query_item, 'ALTER');
if (!$table_name)
{
return false;
}
/* 先把CHANGE操作提取出来执行再过滤掉它们 */
$result = $this->parse_change_query($query_item, $table_name);
if ($result[0] && !$this->db->query($result[0], 'SILENT'))
{
$this->handle_error($result[0]);
return false;
}
if (!$result[1])
{
return true;
}
/* 把DROP [COLUMN]提取出来执行,再过滤掉它们 */
$result = $this->parse_drop_column_query($result[1], $table_name);
if ($result[0] && !$this->db->query($result[0], 'SILENT'))
{
$this->handle_error($result[0]);
return false;
}
if (!$result[1])
{
return true;
}
/* 把ADD [COLUMN]提取出来执行,再过滤掉它们 */
$result = $this->parse_add_column_query($result[1], $table_name);
if ($result[0] && !$this->db->query($result[0], 'SILENT'))
{
$this->handle_error($result[0]);
return false;
}
if (!$result[1])
{
return true;
}
/* 把DROP INDEX提取出来执行再过滤掉它们 */
$result = $this->parse_drop_index_query($result[1], $table_name);
if ($result[0] && !$this->db->query($result[0], 'SILENT'))
{
$this->handle_error($result[0]);
return false;
}
if (!$result[1])
{
return true;
}
/* 把ADD INDEX提取出来执行再过滤掉它们 */
$result = $this->parse_add_index_query($result[1], $table_name);
if ($result[0] && !$this->db->query($result[0], 'SILENT'))
{
$this->handle_error($result[0]);
return false;
}
/* 执行其它的修改操作 */
if ($result[1] && !$this->db->query($result[1], 'SILENT'))
{
$this->handle_error($result[1]);
return false;
}
return true;
}
/**
* 解析出CHANGE操作
*
* @access public
* @param string $query_item SQL查询项
* @param string $table_name 表名
* @return array 返回一个以CHANGE操作串和其它操作串组成的数组
*/
function parse_change_query($query_item, $table_name = '')
{
$result = array('', $query_item);
if (!$table_name)
{
$table_name = $this->get_table_name($query_item, 'ALTER');
}
$matches = array();
/* 第1个子模式匹配old_col_name第2个子模式匹配column_definition第3个子模式匹配new_col_name */
$pattern = '/\s*CHANGE\s*`?(\w+)`?\s*`?(\w+)`?([^,(]+\([^,]+?(?:,[^,)]+)*\)[^,]+|[^,;]+)\s*,?/i';
if (preg_match_all($pattern, $query_item, $matches, PREG_SET_ORDER))
{
$fields = $this->get_fields($table_name);
$num = count($matches);
$sql = '';
for ($i = 0; $i < $num; $i++)
{
/* 如果表中存在原列名 */
if (in_array($matches[$i][1], $fields))
{
$sql .= $matches[$i][0];
}
/* 如果表中存在新列名 */
elseif (in_array($matches[$i][2], $fields))
{
$sql .= 'CHANGE ' . $matches[$i][2] . ' ' . $matches[$i][2] . ' ' . $matches[$i][3] . ',';
}
else /* 如果两个列名都不存在 */
{
$sql .= 'ADD ' . $matches[$i][2] . ' ' . $matches[$i][3] . ',';
$sql = preg_replace('/(\s+AUTO_INCREMENT)/i', '\1 PRIMARY KEY', $sql);
}
}
$sql = 'ALTER TABLE ' . $table_name . ' ' . $sql;
$result[0] = preg_replace('/\s*,\s*$/', '', $sql);//存储CHANGE操作已过滤末尾的逗号
$result[0] = $this->insert_charset($result[0]);//加入字符集设置
$result[1] = preg_replace($pattern, '', $query_item);//存储其它操作
$result[1] = $this->has_other_query($result[1]) ? $result[1]: '';
}
return $result;
}
/**
* 解析出DROP COLUMN操作
*
* @access public
* @param string $query_item SQL查询项
* @param string $table_name 表名
* @return array 返回一个以DROP COLUMN操作和其它操作组成的数组
*/
function parse_drop_column_query($query_item, $table_name = '')
{
$result = array('', $query_item);
if (!$table_name)
{
$table_name = $this->get_table_name($query_item, 'ALTER');
}
$matches = array();
/* 子模式存储列名 */
$pattern = '/\s*DROP(?:\s+COLUMN)?(?!\s+(?:INDEX|PRIMARY))\s*`?(\w+)`?\s*,?/i';
if (preg_match_all($pattern, $query_item, $matches, PREG_SET_ORDER))
{
$fields = $this->get_fields($table_name);
$num = count($matches);
$sql = '';
for ($i = 0; $i < $num; $i++)
{
if (in_array($matches[$i][1], $fields))
{
$sql .= 'DROP ' . $matches[$i][1] . ',';
}
}
if ($sql)
{
$sql = 'ALTER TABLE ' . $table_name . ' ' . $sql;
$result[0] = preg_replace('/\s*,\s*$/', '', $sql);//过滤末尾的逗号
}
$result[1] = preg_replace($pattern, '', $query_item);//过滤DROP COLUMN操作
$result[1] = $this->has_other_query($result[1]) ? $result[1] : '';
}
return $result;
}
/**
* 解析出ADD [COLUMN]操作
*
* @access public
* @param string $query_item SQL查询项
* @param string $table_name 表名
* @return array 返回一个以ADD [COLUMN]操作和其它操作组成的数组
*/
function parse_add_column_query($query_item, $table_name = '')
{
$result = array('', $query_item);
if (!$table_name)
{
$table_name = $this->get_table_name($query_item, 'ALTER');
}
$matches = array();
/* 第1个子模式存储列定义第2个子模式存储列名 */
$pattern = '/\s*ADD(?:\s+COLUMN)?(?!\s+(?:INDEX|UNIQUE|PRIMARY))\s*(`?(\w+)`?(?:[^,(]+\([^,]+?(?:,[^,)]+)*\)[^,]+|[^,;]+))\s*,?/i';
if (preg_match_all($pattern, $query_item, $matches, PREG_SET_ORDER))
{
$fields = $this->get_fields($table_name);
$mysql_ver = $this->db->version();
$num = count($matches);
$sql = '';
for ($i = 0; $i < $num; $i++)
{
if (in_array($matches[$i][2], $fields))
{
/* 如果为低版本MYSQL则把非法关键字过滤掉 */
if ($mysql_ver < '4.0.1' )
{
$matches[$i][1] = preg_replace('/\s*(?:AFTER|FIRST)\s*.*$/i', '', $matches[$i][1]);
}
$sql .= 'CHANGE ' . $matches[$i][2] . ' ' . $matches[$i][1] . ',';
}
else
{
$sql .= 'ADD ' . $matches[$i][1] . ',';
}
}
$sql = 'ALTER TABLE ' . $table_name . ' ' . $sql;
$result[0] = preg_replace('/\s*,\s*$/', '', $sql);//过滤末尾的逗号
$result[0] = $this->insert_charset($result[0]);//加入字符集设置
$result[1] = preg_replace($pattern, '', $query_item);//过滤ADD COLUMN操作
$result[1] = $this->has_other_query($result[1]) ? $result[1] : '';
}
return $result;
}
/**
* 解析出DROP INDEX操作
*
* @access public
* @param string $query_item SQL查询项
* @param string $table_name 表名
* @return array 返回一个以DROP INDEX操作和其它操作组成的数组
*/
function parse_drop_index_query($query_item, $table_name = '')
{
$result = array('', $query_item);
if (!$table_name)
{
$table_name = $this->get_table_name($query_item, 'ALTER');
}
/* 子模式存储键名 */
$pattern = '/\s*DROP\s+(?:PRIMARY\s+KEY|INDEX\s*`?(\w+)`?)\s*,?/i';
if (preg_match_all($pattern, $query_item, $matches, PREG_SET_ORDER))
{
$indexes = $this->get_indexes($table_name);
$num = count($matches);
$sql = '';
for ($i = 0; $i < $num; $i++)
{
/* 如果子模式为空,删除主键 */
if (empty($matches[$i][1]))
{
$sql .= 'DROP PRIMARY KEY,';
}
/* 否则删除索引 */
elseif (in_array($matches[$i][1], $indexes))
{
$sql .= 'DROP INDEX ' . $matches[$i][1] . ',';
}
}
if ($sql)
{
$sql = 'ALTER TABLE ' . $table_name . ' ' . $sql;
$result[0] = preg_replace('/\s*,\s*$/', '', $sql);//存储DROP INDEX操作已过滤末尾的逗号
}
$result[1] = preg_replace($pattern, '', $query_item);//存储其它操作
$result[1] = $this->has_other_query($result[1]) ? $result[1] : '';
}
return $result;
}
/**
* 解析出ADD INDEX操作
*
* @access public
* @param string $query_item SQL查询项
* @param string $table_name 表名
* @return array 返回一个以ADD INDEX操作和其它操作组成的数组
*/
function parse_add_index_query($query_item, $table_name = '')
{
$result = array('', $query_item);
if (!$table_name)
{
$table_name = $this->get_table_name($query_item, 'ALTER');
}
/* 第1个子模式存储索引定义第2个子模式存储"PRIMARY KEY"第3个子模式存储键名第4个子模式存储列名 */
$pattern = '/\s*ADD\s+((?:INDEX|UNIQUE|(PRIMARY\s+KEY))\s*(?:`?(\w+)`?)?\s*\(\s*`?(\w+)`?\s*(?:,[^,)]+)*\))\s*,?/i';
if (preg_match_all($pattern, $query_item, $matches, PREG_SET_ORDER))
{
$indexes = $this->get_indexes($table_name);
$num = count($matches);
$sql = '';
for ($i = 0; $i < $num; $i++)
{
$index = !empty($matches[$i][3]) ? $matches[$i][3] : $matches[$i][4];
if (!empty($matches[$i][2]) && in_array('PRIMARY', $indexes))
{
$sql .= 'DROP PRIMARY KEY,';
}
elseif (in_array($index, $indexes))
{
$sql .= 'DROP INDEX ' . $index . ',';
}
$sql .= 'ADD ' . $matches[$i][1] . ',';
}
$sql = 'ALTER TABLE ' . $table_name . ' ' . $sql;
$result[0] = preg_replace('/\s*,\s*$/', '', $sql);//存储ADD INDEX操作已过滤末尾的逗号
$result[1] = preg_replace($pattern, '', $query_item);//存储其它的操作
$result[1] = $this->has_other_query($result[1]) ? $result[1] : '';
}
return $result;
}
/**
* 获取所有的indexes
*
* @access public
* @param string $table_name 数据表名
* @return array
*/
function get_indexes($table_name)
{
$indexes = array();
$result = $this->db->query("SHOW INDEX FROM $table_name", 'SILENT');
if ($result)
{
while ($row = $this->db->fetchRow($result))
{
$indexes[] = $row['Key_name'];
}
}
return $indexes;
}
/**
* 获取所有的fields
*
* @access public
* @param string $table_name 数据表名
* @return array
*/
function get_fields($table_name)
{
$fields = array();
$result = $this->db->query("SHOW FIELDS FROM $table_name", 'SILENT');
if ($result)
{
while ($row = $this->db->fetchRow($result))
{
$fields[] = $row['Field'];
}
}
return $fields;
}
/**
* 判断是否还有其它的查询
*
* @access private
* @param string $sql_string SQL查询串
* @return boolean 有返回true否则返回false
*/
function has_other_query($sql_string)
{
return preg_match('/^\s*ALTER\s+TABLE\s*`\w+`\s*\w+/i', $sql_string);
}
/**
* 在查询串中加入字符集设置
*
* @access private
* @param string $sql_string SQL查询串
* @return string 含有字符集设置的SQL查询串
*/
function insert_charset($sql_string)
{
if ($this->db->version() > '4.1')
{
$sql_string = preg_replace('/(TEXT|CHAR\(.*?\)|VARCHAR\(.*?\))\s+/i',
'\1 CHARACTER SET ' . $this->db_charset . ' ',
$sql_string);
}
return $sql_string;
}
/**
* 处理其它的数据库操作
*
* @access private
* @param string $query_item SQL查询项
* @return boolean 成功返回true失败返回false。
*/
function do_other($query_item)
{
if (!$this->db->query($query_item, 'SILENT'))
{
$this->handle_error($query_item);
return false;
}
return true;
}
/**
* 处理错误信息
*
* @access private
* @param string $query_item SQL查询项
* @return boolean 成功返回true失败返回false。
*/
function handle_error($query_item)
{
$mysql_error = 'ERROR NO: ' . $this->db->errno()
. "\r\nERROR MSG: " . $this->db->error();
$error_str = "SQL Error:\r\n " . $mysql_error
. "\r\n\r\n"
. "Query String:\r\n ". $query_item
. "\r\n\r\n"
. "File Path:\r\n ". $this->current_file
. "\r\n\r\n\r\n\r\n";
/* 过滤一些错误 */
if (!in_array($this->db->errno(), $this->ignored_errors))
{
$this->error = $error_str;
}
if ($this->log_path)
{
$f = @fopen($this->log_path, 'ab+');
if (!$f)
{
return false;
}
if (!@fwrite($f, $error_str))
{
return false;
}
}
return true;
}
}
?>