SQL Server存储过程执行后PHP变量混入无关文件内容问题
问题:SQL Server存储过程输出参数被PHP文件内容污染
向SQL Server存储过程按引用传递的PHP变量$FormType2,在存储过程执行后,其值混入了另一个PHP文件(Functions.php)的8000字符内容。该文件因包含常用函数被多数PHP文件引入,无法理解其内容为何会在SQL查询后被赋值给该变量。
存储过程会为输出参数@FormType赋值,同时将传入的XML数据插入临时表#NewValues并输出该表。预期执行后$FormType2应返回查询到的FormType值(如Termination),但实际却包含Functions.php从第25位开始的内容。若注释掉存储过程中的SELECT * FROM #NewValues语句,变量值则符合预期。
PHP参数代码
$xmlNewVals = new SimpleXMLElement("<?xml version='1.0'?><data></data>"); array_to_xml($NewVals, $xmlNewVals); echo " XML Data:<br>{$xmlNewVals->asXML()}<br> "; //确认XML数据准确性 unset($FormType2); $FormType2 = "-_-_-_-"; //初始化值 $stmtParams[] = array($PAFIndex, SQLSRV_PARAM_IN); $stmtParams[] = array($TimeStamp, SQLSRV_PARAM_IN); $stmtParams[] = array($xmlNewVals->asXML(), SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_CHAR), SQLSRV_SQLTYPE_XML); $stmtParams[] = array($_SESSION['UserName'], SQLSRV_PARAM_IN); $stmtParams[] = array($NewValReason, SQLSRV_PARAM_IN); $stmtParams[] = array(&$FormType2, SQLSRV_PARAM_INOUT); //按引用传递变量 $stmt = "{call HR.PAFAlterChecks(?".str_repeat(",?", count($stmtParams)-1).")}"; echo " Pre-Execute:<br>{$FormType2}<br> "; //正确显示初始化值 $result = ExecSQL($stmt, $hasrows, $stmtParams); echo " Post-Execute:<br>{$FormType2}<br> "; //包含Functions.php文件的8000字符内容
ExecSQL函数代码
function ExecSQL($SQL, &$hasRows = false, $Parameters = array(), $BypassError = false) { global $presql, $postsql, $SQLconnection, $GenericErrorMessage; unset($Result, $Error); $Error = ""; if(empty($Parameters)){ $SQL = $presql.$SQL.$postsql; } $Result = sqlsrv_query($SQLconnection, $SQL, $Parameters); if(empty($Result)){ $Errors = sqlsrv_errors(); foreach($Errors as $i => $err) { $Error .= $i.": ".$err['message']." "; } $Error .= " ({ ".$SQL." }) "; echo "<br><br> "; print_r($Parameters); //仅测试用,生产环境已注释 echo "<br><br> "; } else { do { $hasRows = sqlsrv_has_rows($Result); } while($hasRows === false && sqlsrv_next_result($Result)); } if(!empty($Error) && empty($BypassError)){ $bug = debug_backtrace(); $res = Error(substr($bug[0]['file'], strrpos($bug[0]['file'], '\\')+1), $Error, $bug[0]['line']); die($GenericErrorMessage." ".$Error); } return $Result; }
存储过程代码
USE [FormsDataTest] GO /****** Object: StoredProcedure [HR].[PAFAlterChecks] Script Date: 05/09/2024 1:09:58 PM ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER PROCEDURE [HR].[PAFAlterChecks] -- 存储过程参数 @PAFIndex INT, @TimeStamp DATETIME = '', @NewVal XML, @Verifier NVARCHAR(50) = '', @NewValReason NVARCHAR(MAX) = '', @FormType NVARCHAR(15) OUT AS BEGIN SET NOCOUNT ON; BEGIN TRY BEGIN TRANSACTION SET @FormType = (SELECT [FormType] FROM HR.PersonnelAction WHERE [index] = @PAFIndex); DECLARE @xmlHandle INT EXEC sys.sp_xml_preparedocument @xmlHandle OUTPUT, @NewVal SELECT @PAFIndex AS [PAFIndex], * INTO #NewValues FROM OPENXML(@xmlHandle, '/data', 2) WITH ([TerminationType] NVARCHAR(50), [TerminationName] NVARCHAR(MAX), [PolicyName] NVARCHAR(MAX), [OtherText] NVARCHAR(MAX) ) EXEC sys.sp_xml_removedocument @xmlHandle SELECT * FROM #NewValues --验证XML输入是否正确 DROP TABLE #NewValues COMMIT TRANSACTION END TRY BEGIN CATCH ROLLBACK TRANSACTION END CATCH END GO
解决方案
核心原因
存储过程返回了两个结果集:首先是输出参数@FormType的赋值逻辑,然后是SELECT * FROM #NewValues的查询结果。而ExecSQL函数仅遍历到第一个有行的结果集就停止,未处理完所有结果集,导致sqlsrv扩展内部资源指针混乱,进而污染了按引用传递的输出变量。
具体修复方案
方案1:遍历所有结果集(推荐)
修改ExecSQL函数,确保处理完存储过程返回的所有结果集,避免资源泄漏和变量污染:
else { // 遍历所有结果集,直到没有更多结果 do { $hasRows = sqlsrv_has_rows($Result); // 即使结果集没有行,也需要推进指针 } while (sqlsrv_next_result($Result)); }
方案2:移除存储过程中的调试查询
如果SELECT * FROM #NewValues仅用于调试,可注释或删除该语句,确保存储过程只返回输出参数结果:
-- SELECT * FROM #NewValues -- 注释掉这条调试语句
方案3:显式指定输出参数的SQL类型
在定义输出参数时,明确指定SQL类型为NVARCHAR(15),避免类型不匹配导致的内存问题:
$stmtParams[] = array(&$FormType2, SQLSRV_PARAM_INOUT, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), SQLSRV_SQLTYPE_NVARCHAR(15));
内容的提问来源于stack exchange,提问作者Jack Duece
相关产品推荐
相关产品推荐

