使用php脚本检查一个表中的重复记录并将重复记录插入到另一个表中

问题描述

我有两个下表

1)CandidateDetails

EmployeeId  FirstName       LastName    Mobile          Email     BatchId
1           fn1             ln1         123456789       Email1      75
2           fn2             ln2         123456790       Email2      75
3           fn3             ln3         123456791       Email3      75
4           fn4             ln4         123456792       Email4      75
                

2)DuplicateCandidate

EmployeeId  FirstName       LastName    Mobile          Email     BatchId

我的要求是,如果 CandidateDetails 表中已经存在EmployeeId,则将重复记录插入 DuplicateCandidate

下面是PHP脚本,仅用于将数组插入到 CandidateDetails 表中,但是我无法检查条件是否为雇员ID是否重复,然后将该记录插入到另一个 DuplicateCandidate 表。

<?PHP
header("Access-Control-Allow-Origin: http://localhost:4200");
header("Access-Control-Allow-Credentials: true ");
header('Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS');
header("Access-Control-Allow-Headers: X-Custom-Header,Origin,Content-Type,Authorisation,X-Requested-With");
header("Content-Type: application/json; charset=UTF-8 ");

$json = file_get_contents('PHP://input');
$decoded = json_decode($json,true);

print_r($decoded);
 $id=$_GET['id'];

function conn() {
  $dbhost = "xxxx";
  $user = "xxx";
  $pass = "xxx";
  $db = "xxxx";
  $conn = new PDO('MysqL:host=xxx;dbname=xxx',$user,$pass);
  return $conn;
}
$db = conn();
$db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);                  
$p = $db->prepare("INSERT INTO CandidateDetails(EmployeeId,FirstName,LastName,Mobile,Email,BatchId)VALUES(:EmployeeId,:FirstName,:LastName,:Mobile,:Email,'$id')");
                   
foreach ($decoded as $item) {
  $p->execute($item);
}

echo json_encode(true);

?>

解决方法

<?php
header("Access-Control-Allow-Origin: http://localhost:4200");
header("Access-Control-Allow-Credentials: true ");
header('Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS');
header("Access-Control-Allow-Headers: X-Custom-Header,Origin,Content-Type,Authorisation,X-Requested-With");
header("Content-Type: application/json; charset=UTF-8 ");

$json = file_get_contents('php://input');
$decoded = json_decode($json,true);

print_r($decoded);
$id = $_GET['id'];

function conn()
{
    $dbhost = "x.x.x.x";
    $user = "x";
    $pass = "x";
    $db = "x";
    $conn = new PDO('mysql:host=x;dbname=x',$user,$pass);
    return $conn;
}
$db = conn();
$db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
foreach ($decoded as $item)
{
    echo $item['EmployeeId'];

    $sql = "SELECT * FROM CandidateDetails WHERE EmployeeId = " . $item['EmployeeId'];
    //$result = $conn-                         >query($sql);
    echo $item['EmployeeId'];
    echo $sql;
    $result = $db->query($sql);
    //echo $result;
    //echo $result->num_rows;
    $count = $result->rowCount();
    echo $count;
    if ($count > 0)
    {
        //DuplicateCandidate
        echo "duplicte results";
        //$p = $db->prepare("INSERT INTO CandidateDetailsLog(EmployeeId,FirstName,LastName,Mobile,Email,BatchId)VALUES(:EmployeeId,:FirstName,:LastName,:Mobile,:Email,'$id')");
        $p = $db->prepare("INSERT INTO CandidateDetailsLog(EmployeeId,BatchId)
                                      VALUES ('" . $item['EmployeeId'] . "','" . $item['FirstName'] . "','" . $item['LastName'] . "','" . $item['Mobile'] . "','" . $item['Email'] . "','" . $id . "')");

        $p->execute();
    }
    else
    {
        echo "0 results";
        //$p = $db->prepare("INSERT INTO CandidateDetails(EmployeeId,'$id')");
        $p = $db->prepare("INSERT INTO CandidateDetails(EmployeeId,'" . $id . "')");

        $p->execute();
    }

}

$o = $db->prepare("UPDATE BatchDetailsInfo SET BatchStatus='B2' WHERE BatchId='$id'");
$o->execute();

echo json_encode(true);

?>
,

尽管可接受的答案可能会起到作用,但它具有多个SQL injection漏洞,包括$_GET['id'](不应该被信任-恶意行为者可以在此处放置任何内容,包括SQL命令以清除您的数据库),以及从php://input收集的JSON(同样-即使是有效的JSON,您也可以在此处隐藏用于删除数据的命令)。参数转义(如您的原始示例)是解决方法。

<?php

/* Your headers etc here*/

///Build a database connection
function db_conn() {
    /* Your Mysql setup with user/pass/db etc in a PDO object here*/
    $db=new PDO('mysql:host=x;dbname=x',$pass);
    //You might want to set this here (for all connections:)
    //$db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
    return $db;
}

///Search the candidate table for an entry for the given ID
function db_employeeCandidateExists($db,$dangerEmployeeId) {
    $stmt=$db->prepare('SELECT EmployeeId FROM CandidateDetails WHERE EmployeeId=?');
    $stmt->execute([$dangerEmployeeId]);
    $result=$stmt->fetchAll();//We're expecting 0 rows (not found) or 1 row with the ID if it's a dup
    return count($result)>0;
}

///Add a row to the DuplicateCandidate table
function db_addDuplicate($db,$dangerRow,$dangerBatchId) {
    //All columns you want to fill out - layed out to be visually obvious how many there are
    $cols=['EmployeeId','FirstName','LastName','Mobile','Email','BatchId'];
    //Values for the above columns - layed out to be visually equal to above
    // don't forget
    $vals=[$dangerRow['EmployeeId'],$dangerRow['FirstName'],$dangerRow['LastName'],$dangerRow['Mobile'],$dangerRow['Email'],$dangerBatchId];
    //The parameters can use a count of the cols (above)
    $params=substr(str_repeat('?,',count($cols)),-1);

    $stmt=$db->prepare('INSERT INTO DuplicateCandidate ('.
        implode(',$cols).
        ') VALUES ('.
        $params.
        ')');

    $stmt->execute($vals);
    //todo: You might want to check there are no SQL errors reported here
}

///Add a row to the CandidateDetails table
function db_addCandiate($db,-1);

    $stmt=$db->prepare('INSERT INTO CandidateDetails ('.
        implode(',$cols).
        ') VALUES ('.
        $params.
        ')');

    $stmt->execute($vals);
    //todo: You might want to check there are no SQL errors reported here
}

///Get JSON from input and decode it into an associative array
function getJson() {
    $json = file_get_contents('php://input');
    return json_decode($json,true);
}

$db = db_conn();
//You might want to set this inside the `conn()` method for all usage?
$db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

// *** *** Dangerous CONTENT *** ***
// Both $id (presuambly an integer) and $json (an associative array of data)
// are user-provided and therefore the cannot be trusted - you NEED to escape these
// values before using them in SQL
$dangerBatchId = $_GET['id'];
$dangerJson = getJson();

foreach($dangerJson as $dangerItem)
{
    if (db_employeeCandidateExists($db,$dangerItem['EmployeeId'])) {
        //Duplicate
        db_addDuplicate($db,$dangerItem,$dangerBatchId);
        echo 'Applicant '.$dangerItem['EmployeeId']." was a duplicate\n";
    } else {
        db_addCandiate($db,$item,$dangerBatchId);
        echo 'Applicant '.$dangerItem['EmployeeId']." added\n";
    }
}
// Further processing

我使用了位置参数转义(?),该参数也应与MySQL一起使用。命名位置转义(:id)可能更好,但不允许我快速生成所有参数(构建$params字符串),而我测试的数据库不支持它们。>

将组件分为功能(如我所完成的)后,您会注意到我们有一些以db_开头的功能分组……您可以改为将它们捆绑成一个类。那时,您可以避免将$db作为第一个参数,因为您可以在类中内部共享它。 但是,这是一个完全不同的主题,超出了原始问题的范围。