将资料库常用的方法打包起来~
PHPUnit不太熟,虽然都是绿灯,但在资料库方面不知道怎么测XD
听说用mock? 但mock的存取跟资料库存取应该无关吧,顶多介面一样....
直接上code
<?php/** * 辅助MySQLi使用。 * @author 舜<wewe987001@gmail.com> * @version 1.0.0 */class MySqlHelper{ /* 使用教学 一.载入方式 require_once('MySqlHelper.php'); 二.使用方式,直接使用不用宣告与初始化 1.查询: 出来的结果自行mysqli_fetch_array $query = MySqlHelper::query('...sql...'); 2.查询2: 应用在巢状迴圈 while($row1 = MySqlHelper::fetch_array('任意乱数值', '...sql-1...')){ while($row2 = MySqlHelper::fetch_array('别跟上面重複就可以了', '...sql-2...')){ //$row2 } } 3.插入 : 参数形式 key-value MySqlHelper::insert(资料表,参数) $result = MySqlHelper::insert('admindata', ['adminame' => '_test', 'adminpass' => 'test1']); 4.插入 : 参数形式 栏位与值分开 MySqlHelper::insert(资料表, 栏位阵列, 值阵列) $result = MySqlHelper::insert('admindata', ['adminame', 'adminpass'], ['_test', '(select 123)']); 5.插入 : 值为sql(子查询,需用括号包起来) MySqlHelper::insert(资料表, 栏位阵列, 值阵列) $result = MySqlHelper::insert('admindata', ['adminame' => '(select 123)', 'adminpass' => '(select 123)']); 6 新增/更新/删除用法一样,key-value形式与栏位与值分开形式二选一,更新的参数先资料 后条件 7.更新 : 参数形式 key-value MySqlHelper::update('admindata', ['adminpass' => '__123__'], ['adminame' => $adminame]); //无须条件就放空阵列 MySqlHelper::update('admindata', ['adminpass' => '(adminpass)'], []); 8.更新 : 参数形式 栏位与值分开 MySqlHelper::update('admindata', ['adminpass'], ['__123__'], ['adminame'], [$adminame]); //无须条件就放空阵列 MySqlHelper::update('admindata', ['adminpass'], ['(adminpass)'], [], []); */ protected static $connection; protected static $host; protected static $dbname; protected static $user; protected static $password; protected static $fetchStack = []; protected static $fetchStackSql = []; public function __construct($host = '', $dbname = '', $user = '', $password = '') { // 是否没传任何参数 $isEmpty = array_reduce(func_get_args(), function ($v1, $v2) { return $v1 && $v2 === ''; }, true); // 若有值就覆盖并重设 if (!$isEmpty) self::reset($host, $dbname, $user, $password); } // ========================== // 连接资料库 // ========================== /** 处理MySqli连接字串 */ public static function connect() { if (!isset(self::$connection)) { // p: 永久连线 self::$connection = mysqli_connect('p:' . self::$host, self::$user, self::$password, self::$dbname); if (isset(self::$connection)) mysqli_query(self::$connection, "SET NAMES utf8"); } return self::$connection; } public static function reset($host, $dbname, $user, $password) { self::$host = $host ?: self::$host; self::$dbname = $dbname ?: self::$dbname; self::$user = $user ?: self::$user; self::$password = $password ?: self::$password; // 关掉先前连线 if (isset(self::$connection)) self::$connection->close(); // 关闭先前连线 self::$connection = null; // 清空结果集 self::clearFetchStack(); //重新连线 self::connect(); } // ========================== // 资料集处理 // ========================== /** * 清空资料集 * @param 唯一值,预设空值代表全部清空 */ protected static function clearFetchStack($id = '') { if ($id === '') { self::$fetchStack = []; self::$fetchStackSql = []; } else if (isset(self::$fetchStack[$id])) { unset(self::$fetchStack[$id]); unset(self::$fetchStackSql[$id]); } } /** 取得资料集 * @param string $id 资料集的id * @param string $sql 验证用,若sql不同就重设 */ protected static function GetFetchStack($id, $sql) { // 如果有此id 且( 没给sql 或是 sql一样),就取旧的 if (array_key_exists($id, self::$fetchStack) && (empty($sql) || self::$fetchStackSql[$id] == $sql)) return self::$fetchStack[$id]; else { // 若有不同就新增 self::$fetchStack[$id] = self::query($sql); self::$fetchStackSql[$id] = $sql; return self::$fetchStack[$id]; } } // ========================== // 查询/捞资料 // ========================== /** 执行查询(自行fetch取结果) * @param string $sql * @param bool $bigData 是否是大资料,预设false * @return mysqli_result|true|false 回传 */ public static function query($sql, $bigData = false) { if (!$bigData) $query = mysqli_query(self::connect(), $sql); else $query = mysqli_query(self::connect(), $sql, MYSQLI_USE_RESULT); return $query; } /** 查询全部资料 * @param string $sql * @param int $resulttype 预设 MYSQLI_BOTH,建议用 MYSQLI_ASSOC * @return array */ public static function queryAll($sql, $resulttype = MYSQLI_BOTH){ $result = array(); $rand = rand(); while ($row = self::fetch_array($rand ,$sql, $resulttype)){ $result[] = $row; } return $result; } /** 依序捞资料,提取结果行作为关联数组,数字数组或两者(mysqli_fetch_array) * @param string $id 唯一值,随意给个乱数即可;因应可能应用在巢状迴圈中,为避免跑错结果集,所以用$id来分辨谁是谁 * @param string $sql * @param int $resulttype 预设MYSQLI_BOTH,提取结果行作为关联数组,数字数组或两者 * @return array|null */ public static function fetch_array($id, $sql, $resulttype = MYSQLI_BOTH) { // 取得或新增堆叠 $query = self::GetFetchStack($id, $sql); // 捞资料 $result = mysqli_fetch_array($query, $resulttype); // 如果跑完了就从结果集移除 if (!$result) self::clearFetchStack($id); return $result; } /**依序捞资料,获取结果行作为关联数组(mysqli_fetch_assoc) * @param string $id 唯一值,随意给个乱数即可;因应可能应用在巢状迴圈中,为避免跑错结果集,所以用$id来分辨谁是谁 * @param string $sql * @return array|null */ public static function fetch_assoc($id, $sql) { return self::fetch_array($id, $sql, MYSQLI_ASSOC); } /**依序捞资料,获取结果行作为枚举数组(mysqli_fetch_row) * @param string 唯一值,随意给个乱数即可;因应可能应用在巢状迴圈中,为避免跑错结果集,所以用$id来分辨谁是谁 * @param string sql * @return array|null */ public static function fetch_row($id, $sql) { return self::fetch_array($id, $sql, MYSQLI_NUM); } // ========================== // 新增/更新/删除 // 1.用法可选择用key-value 或是 keys + valuies // 举例: // (1)栏位名称与值配对: MySqlHelper::insert('tableName', ['field1'=>'value1', 'field2'=>'value2', ...]) // (2)栏位名称与值分开: MySqlHelper::delete('tableName', ['field1', 'field2', ...], ['value1', 'value2', ...]) // 2.更新的用法一样,先资料 后条件 // 举例: // (1)栏位名称与值配对:MySqlHelper::update('tableName' ,['field1'=>'value1', 'field2'=>'value2'] ,['where3'=>'value3'] ) // (2)栏位名称与值分开:MySqlHelper::update('tableName',['dataField1', 'dataField2'], ['dataValue1', 'dataValue2']',['whereField1', 'whereField2'], ['whereValue1', 'whereValue2']) // 3.值若是sql,请用括号包起来 // 举例: // MySqlHelper::insert('tableName', ['field1'=>'(select 2)']) // ========================== /** * 新增用法 * (1)key-value:ex. insert('tableName', ['field1'=>'value1', 'field2'=>'value2']) * (2)keys and valuies:ex. insert('tableName', ['field1', 'field2'], ['value1', 'value2']) * (3)值若是sql,请用括号包起来 ex. insert('tableName', ['field1'=>'(select 2)']) */ public static function insert($table, array $keys_Or_KeyVelue, array $values = []) { // 前两个参数必填 if (empty($table) || !count($keys_Or_KeyVelue)) return false; // 若为keys + valuies模式,参数数量对不上就报错,对地上就改为key-value形式 if (count($values) && (count($values) != count($keys_Or_KeyVelue))) return false; // key-value形式拆成 keys+valuies if (!count($values)) { $values = array_values($keys_Or_KeyVelue); $keys_Or_KeyVelue = array_keys($keys_Or_KeyVelue); } // 处理值的格式 $values = array_map(function($val){return self::escapeSqlString($val);}, $values); // 组合SQL $sql = 'insert into ' . $table . '(' . join(',', $keys_Or_KeyVelue) . ')values(' . join(',', $values) . ')'; return self::query($sql); } /** * 删除用法 * (1)key-value:ex. delete('tableName', ['field1'=>'value1', 'field2'=>'value2']) * (2)keys and valuies:ex. delete('tableName', ['field1', 'field2'], ['value1', 'value2']) * (3)值若是sql,请用括号包起来 ex. delete('tableName', ['field1'=>'(select 2)']) */ public static function delete($table, array $keys_Or_KeyVelue, array $values = []) { // 前两个参数必填 if (empty($table) || !count($keys_Or_KeyVelue)) return false; // 若为keys + valuies模式,参数数量对不上就报错 if (count($values) && (count($values) != count($keys_Or_KeyVelue))) return false; // 值阵列是空的表示是key-value形式,key-value形式拆成 keys+valuies if (!count($values)) { $values = array_values($keys_Or_KeyVelue); $keys_Or_KeyVelue = array_keys($keys_Or_KeyVelue); } // 处理值的格式 $values = array_map(function($val){return self::escapeSqlString($val);}, $values); // 组合条件SQL $where =''; foreach ($keys_Or_KeyVelue as $index => $field) { $where .= $field . '=' . $values[$index] . ' and '; } if(count($where)){ $where = ' where '. substr($where, 0, -4); } // 组合SQL $sql = 'delete from ' . $table .$where; return self::query($sql); } /** * 更新用法,值若是sql,请用括号包起来 * (1)key-value:ex. update('tableName' ,['field1'=>'value1', 'field2'=>'value2'] ,['where3'=>'value3'] ) * (2)keys and valuies:ex. update('tableName',['dataField1', 'dataField2'], ['dataValue1', 'dataValue2']',['whereField1', 'whereField2'], ['whereValue1', 'whereValue2']) * @param string 资料表(包含join) * @param array 放置 资料key-value 或 资料栏位名称 * @param array 放置 条件key-value 或 资料内容 * @param array 放置 条件栏位名称 * @param array 放置 条件内容 */ public static function update($table, array ...$param) { // 没给表格 与 阵列数量不对(key-value形式有两个,栏位内容分开有4个)就离开 if (empty($table) || count($param) <= 1 || count($param) == 3 || count($param) > 4) return false; // 转成key-value形式 if(count($param)==4){ $data = array_combine($param[0], $param[1]) ?: []; $where = array_combine($param[2], $param[3]) ?: []; }else{ $data = &$param[0]; $where = &$param[1]; } $sql ='update '.$table.' set '; //资料处理 foreach ($data as $key => $value) { // 防注入处理 $sql .= $key . '=' . self::escapeSqlString($value) .','; } $sql = substr($sql, 0, -1); //条件处理 if(count($where)){ $sql .= ' where '; foreach ($where as $key => $value) { // 防注入处理 $sql .= $key . '=' . self::escapeSqlString($value) .'and '; } $sql = substr($sql, 0, -4); } return self::query($sql); } // ========================== // 共通 // ========================== /** sql字串处理,mysqli内建的防範注入攻击 */ protected static function escapeSqlString($val){ $val = trim($val); // 排除开头空白 // 这几个开头的不处理或特殊处理【 ( ' " ` 】 $first = substr($val, 0, 1); switch ($first) { case '(': // sql case '`': // 被视为资料栏位名称 return $val; case '"': case '\'': $val = trim($val, '\'"'); default: // 防注入 return "'" . mysqli_real_escape_string(self::connect(), $val) . "'"; break; } }}
PHPUnit
<?phpuse PHPUnit\Framework\TestCase;include_once 'MySqlHelper.php';class MySqlHelperTest extends TestCase{ /** 查询(自行fetch) */ public function test_query() { // assertions $query = MySqlHelper::query("select 1 from admindata where adminame='admin'"); list($result) = mysqli_fetch_row($query); $this->assertTrue($result == '1'); } /** 查询全部资料 */ public function test_queryAll() { // assertions $query = MySqlHelper::queryAll("select 1 from admindata"); $this->assertTrue(count($query) == 3); } /* 巢状迴圈应用,内外不相干 */ public function test_fetch_array1() { $msg = ''; while ($row1 = MySqlHelper::fetch_array('任意乱数值', 'select adminame from admindata')) { $msg .= $row1['adminame'] . ':'; while ($row2 = MySqlHelper::fetch_assoc('别跟上面重複就可以了', 'select adminpass from admindata')) { $msg .= $row2['adminpass'] . ','; } $msg .= ';'; } $this->assertTrue($msg == 'admin:practice,turrteled,teaching99,;turtle:practice,turrteled,teaching99,;ADM:practice,turrteled,teaching99,;'); } /* 巢状迴圈应用,内层随外层影响 */ public function test_fetch_array2() { $msg = ''; while ($row1 = MySqlHelper::fetch_array('任意乱数值', 'select adminame from admindata')) { $msg .= $row1['adminame'] . ':'; while ($row2 = MySqlHelper::fetch_assoc('别跟上面重複就可以了', 'select adminpass from admindata where adminame=\'' . $row1['adminame'] . '\'')) { $msg .= $row2['adminpass'] . ','; } $msg .= ';'; } $this->assertTrue($msg == 'admin:practice,;turtle:turrteled,;ADM:teaching99,;'); } /** 插入验证 * (1)验证key-value形式 * (2)验证栏位与值分开测试形式 * (2)验证内容是sql(子查询) */ public function test_insert() { // assertions $table = 'admindata'; $adminame = '_test'; $data1 = ['adminame' => $adminame, 'adminpass' => 'test1']; $fild2 = ['adminame', 'adminpass']; $data2 = [$adminame, 'test2']; $testData3 = ['adminame' => $adminame, 'adminpass' => '(select \'test3\')']; // 测试一:key-value测试 $result1 = MySqlHelper::insert($table, $data1); $this->assertTrue($result1, 'key-value测试 失败'); // 测试一:验证 $review1 = current(MySqlHelper::queryAll("select 1 from admindata where adminpass='test1'", MYSQLI_ASSOC)); $this->assertTrue(current($review1) == '1', 'key-value测试 验证失败'); // 测试二:栏位与值分开测试 $result2 = MySqlHelper::insert($table, $fild2, $data2); $this->assertTrue($result2, '栏位与值分开测试 失败'); // 测试二:验证 $review2 = current(MySqlHelper::queryAll("select 1 from admindata where adminpass='test2'", MYSQLI_ASSOC)); $this->assertTrue(current($review2) == '1', '栏位与值分开测试 验证失败'); // 删掉测试资料 $query = MySqlHelper::query("delete from admindata where adminame='$adminame'"); // 测试三:内容是sql(子查询) $result3 = MySqlHelper::insert($table, $testData3); $this->assertTrue($result3, '内容是sql(子查询) 失败'); // 测试三:验证 $review3 = current(MySqlHelper::queryAll("select adminpass from admindata where adminame='$adminame'", MYSQLI_ASSOC)); $this->assertTrue(current($review3) == 'test3', '内容是sql(子查询)测试 验证失败'); // 删掉测试资料 // $query = MySqlHelper::query("delete from admindata where adminame='$adminame'"); $query = MySqlHelper::delete('admindata', ['adminame'], [$adminame]); } /** 综合测试1:新增/更新/删除 key-value形式 */ public function test_complex1() { $adminame = '_test'; $oldPasswd = 'test1'; $newPasswd = 'AAAAAAAA'; //新增测试资料 MySqlHelper::insert('admindata', ['adminame' => $adminame, 'adminpass' => $oldPasswd]); //验证 $review1 = current(MySqlHelper::queryAll("select 1 from admindata where adminame='$adminame' and adminpass='$oldPasswd'", MYSQLI_ASSOC)); $this->assertTrue(current($review1) == '1', '新增测试资料失败'); //更新测试资料 MySqlHelper::update('admindata', ['adminpass' => $newPasswd], ['adminame' => $adminame]); //验证 $review1 = current(MySqlHelper::queryAll("select 1 from admindata where adminame='$adminame' and adminpass='$newPasswd'", MYSQLI_ASSOC)); $this->assertTrue(current($review1) == '1', '更新密码失败'); //删掉测试资料 MySqlHelper::delete('admindata', ['adminame'=>$adminame,'adminpass'=>$newPasswd]); //验证 $review1 = MySqlHelper::queryAll("select 1 from admindata where adminame='$adminame' and adminpass='$newPasswd'", MYSQLI_ASSOC); $this->assertTrue(count($review1) == 0, '删除测试资料失败'); } /** 综合测试2:新增/更新/删除 栏位内容分开形式 */ public function test_complex2() { $adminame = '_test'; $oldPasswd = 'test1'; $newPasswd = 'AAAAAAAA'; //新增测试资料 MySqlHelper::insert('admindata', ['adminame', 'adminpass'], [$adminame, $oldPasswd]); //验证 $review1 = current(MySqlHelper::queryAll("select 1 from admindata where adminame='$adminame' and adminpass='$oldPasswd'", MYSQLI_ASSOC)); $this->assertTrue(current($review1) == '1', '新增测试资料失败'); //更新测试资料 MySqlHelper::update('admindata', ['adminpass'], [$newPasswd], ['adminame'], [$adminame]); //验证 $review1 = current(MySqlHelper::queryAll("select 1 from admindata where adminame='$adminame' and adminpass='$newPasswd'", MYSQLI_ASSOC)); $this->assertTrue(current($review1) == '1', '更新密码失败'); //删掉测试资料 MySqlHelper::delete('admindata', ['adminame','adminpass'], [$adminame,$newPasswd]); //验证 $review1 = MySqlHelper::queryAll("select 1 from admindata where adminame='$adminame' and adminpass='$newPasswd'", MYSQLI_ASSOC); $this->assertTrue(count($review1) == 0, '删除测试资料失败'); }}