一开始我们所用代码选用了我们之中较好的我的搭档的代码,就是代码量稍微大了些 但是实现的功能更全。
这次php版的四则运算出炉了
由于c++代码转php代码有个BUG调不出来,首先,环境:
WampServer 3.0 64bit
netbeans 8.1
然后,数据库设计,
三个表:用户表user,管理员表admin,答题记录表ansrecord
表结构在这里
/* Navicat MySQL Data Transfer Source Server : tp Source Server Version : 50709 Source Host : localhost:3306 Source Database : 2zhuzi Target Server Type : MYSQL Target Server Version : 50709 File Encoding : 65001 Date: 2016-03-31 16:22:23 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `admin` -- ---------------------------- DROP TABLE IF EXISTS `admin`; CREATE TABLE `admin` ( `adminid` int(11) NOT NULL AUTO_INCREMENT, `adminname` varchar(255) NOT NULL, `pwd` varchar(255) NOT NULL, PRIMARY KEY (`adminid`) ) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; -- ---------------------------- -- Records of admin -- ---------------------------- INSERT INTO `admin` VALUES ('1', 'admin', '123456'); INSERT INTO `admin` VALUES ('2', 'root', '123456'); -- ---------------------------- -- Table structure for `ansrecord` -- ---------------------------- DROP TABLE IF EXISTS `ansrecord`; CREATE TABLE `ansrecord` ( `ansrecordid` int(11) NOT NULL AUTO_INCREMENT, `userid` int(11) NOT NULL, `question` varchar(255) NOT NULL, `rightans` double NOT NULL, `lastans` varchar(255) NOT NULL, PRIMARY KEY (`ansrecordid`), KEY `userid` (`userid`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- ---------------------------- -- Records of ansrecord -- ---------------------------- -- ---------------------------- -- Table structure for `user` -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `userid` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `pwd` varchar(255) NOT NULL, `email` varchar(255) NOT NULL, PRIMARY KEY (`userid`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
再然后,用户部分:
包括用户登录,用户注册,用户首页三个页面,可实现用户注册,登陆,注销,跳转到在线答题,访问隔离(好像叫这么个名字)等功能,
接下来是管理员部分:
包括 管理员登录,管理员首页,用户列表,答题记录列表四个页面,可实现管理员的登录,注销,查看用户列表,删除特定用户,查看答题记录,删除指定答题记录等功能。
最后,在线答题部分:
这部分就是核心包括OJ首页,设置题目各种属性,题目列表页面,旧题浏览页面,可实现题目属性设定,服务器端生成题目,旧题浏览,保存答题结果等功能。
题目生成是questionGenerator这个类完成的。
由于xdebug+netbeans/phpstrom/sublime/zendstudio 什么什么的环境弄不起来,所以在线判断这部分功能就阉了。。 其实写好了调了好久调不对。。
下面是我们的核心代码
1 <?php 2 class questionGenerator 3 { 4 private $itemNum = 5; 5 private $isMulandDiv = true; 6 private $isParentheses = true; 7 private $isNeg = true; 8 private $isRem = true; 9 private $start = 0; 10 private $end = 100; 11 public $items = array(); 12 function __construct($itemNum, $isMulandDiv, $isParentheses, $isNeg, $isRem, $start, $end) { 13 $this->itemNum = $itemNum; 14 $this->isMulandDiv = $isMulandDiv; 15 $this->isParentheses = $isParentheses; 16 $this->isNeg = $isNeg; 17 $this->isRem = $isRem; 18 $this->start = $start; 19 $this->end = $end; 20 } 21 22 function isTrueFraction($numerator,$denominator){ //判断产生的分数是否是真分数 23 if($numerator >= $denominator){ 24 return false; 25 } 26 for($i = 2; $i <= $numerator;$i++){ //判断分数是否能够约分 27 if(($numerator % $i == 0)&&($denominator % $i == 0)){ 28 return false; 29 } 30 } 31 return true; 32 } 33 34 function getNum($start = 0, $end = 100,$isParentheses = false,$depth = 0){ // 若带括号 则为 a op ( b op c ) 的形式 op为运算符 35 if($isParentheses){ 36 $num1 = mt_rand($start,$end); 37 if($depth < 2){ //控制递归层数,带括号的式子少于10个 38 $num2 = "( " . $this->getNum($start,$end,mt_rand() % 2 == 0,$depth + 1) . " )"; 39 return strval($num1) . " , " . $num2; 40 }else{ 41 $num2 = "( " . $this->getNum($start,$end,false,$depth + 1) . " )"; 42 return strval($num1) . " , " . $num2; 43 } 44 }else{ 45 if(mt_rand() % 7 == 0){ //若随机数n是7的倍数,产生一个真分数和一个整数,否则为两个整数 46 $num1 = mt_rand($start,$end); 47 $num2 = mt_rand($start,$end); 48 $num3 = mt_rand($start,$end); 49 if($this->isTrueFraction($num1,$num2)){ 50 return strval($num1) . "/" . strval($num2) . " , " . strval($num3); 51 }else{ 52 return $this->getNum($start,$end); 53 } 54 }else{ 55 $num1 = mt_rand($start,$end); 56 $num2 = mt_rand($start,$end); 57 return strval($num1) . " , " . strval($num2); 58 } 59 } 60 } 61 62 function getOperator($num2 = '1',$isMulandDiv = true){ // 默认第二个参数不为0,默认包括乘除法 63 $op = array('+','-','*','/'); 64 if($isMulandDiv){ 65 if($num2 == '0'){ //避免除数为0 66 return $op[mt_rand() % 3]; 67 }else{ 68 return $op[mt_rand() % 4]; 69 } 70 }else{ 71 return $op[mt_rand() % 2]; 72 } 73 } 74 75 function isNegative($num1, $num2, $op){ 76 if($op == '-'){ 77 if(intval($num1) < intval($num2)){ 78 return true; 79 }else{ 80 return false; 81 } 82 }else{ 83 if(intval($num1) + intval($num2) < 0){ 84 return true; 85 }else{ 86 return false; 87 } 88 } 89 } 90 91 function isRemainder($num1, $num2){ 92 if(intval($num1) % intval($num2) == 0){ 93 return false; 94 }else{ 95 return true; 96 } 97 } 98 99 function generateQuest(){ 100 $addFlag = false; 101 $one = 1; 102 while (count($this->items) < $this->itemNum){ 103 $num = $this->getNum($this->start,$this->end,$this->isParentheses); 104 while (strpos($num,",")){ 105 $addFlag = true; 106 if(substr($num,intval(strpos($num,",")) + 2,1) == '('){ //运算符后紧跟括号,运算符选取只和isMulandDiv有关 107 $op = $this->getOperator('1',$this->isMulandDiv); 108 $num = str_replace(",",$op,$num,$one); 109 }else{ //运算符后是数字,运算符选取和num2和isMulandDiv有关,此时是不带括号或最右边的算式 110 $num2 = substr($num, intval(strrpos($num,",")) + 2, intval(strpos($num,")")) - intval(strrpos($num,",")) - 3); 111 $op = $this->getOperator($num2,$this->isMulandDiv); 112 $num = str_replace(",", $op, $num, $one); 113 $begin = 0; //找到形如 a op b 的式子 114 if(strpos($num, "(")){ //如果式子里有()的话 115 $begin = intval(strrpos($num, "(")) + 2; 116 } 117 $num1 = substr($num, $begin, intval(strrpos($num, $op)) - $begin - 1); 118 if( $op == '-' || $op == '+') 119 { 120 if(!$this->isNeg && $this->isNegative($num1, $num2, $op)) 121 { 122 $addFlag = FALSE; 123 break; 124 } 125 }elseif ($op == '/') { 126 if(!$this->isRem && $this->isRemainder($num1, $num2)){ 127 $addFlag = FALSE; 128 break; 129 } 130 } 131 } 132 } 133 if(!$addFlag){ //满足要求,可以添加 134 continue; 135 } 136 if(!in_array($num, $this->items)){ //判断是否重复,不重复则添加 137 array_push($this->items, $num); 138 } 139 } 140 } 141 142 function calculate($num1,$num2,$op){ 143 switch ($op){ 144 case "+": 145 return $num1 + $num2; 146 break; 147 case "-": 148 return $num1 - $num2; 149 break; 150 case "*": 151 return $num1 * $num2; 152 break; 153 case "/": 154 return $num1 / $num2; 155 break; 156 default : 157 echo "<script>alert('Calculate Error !!!');location='setattr.php';</script>"; 158 } 159 } 160 161 function getResult(){ 162 $pri = array( 163 '+' => array('+' => '>', '-' => '>', '*' => '<', '/' => '<', '(' => '<', ')' => '>', '#' => '>'), 164 '-' => array('+' => '>', '-' => '>', '*' => '<', '/' => '<', '(' => '<', ')' => '>', '#' => '>'), 165 '*' => array('+' => '>', '-' => '>', '*' => '>', '/' => '>', '(' => '<', ')' => '>', '#' => '>'), 166 '/' => array('+' => '>', '-' => '>', '*' => '>', '/' => '>', '(' => '<', ')' => '>', '#' => '>'), 167 '(' => array('+' => '<', '-' => '<', '*' => '<', '/' => '<', '(' => '<', ')' => '=', '#' => ''), 168 ')' => array('+' => '>', '-' => '>', '*' => '>', '/' => '>', '(' => '', ')' => '>', '#' => '>'), 169 '#' => array('+' => '<', '-' => '<', '*' => '<', '/' => '<', '(' => '<', ')' => '', '#' => '=') 170 ); 171 $ansL = array(); 172 foreach ($this->items as $exp){ 173 $stackOps = array(); 174 $stackNums = array(); 175 array_push($stackOps,"#"); 176 $exp = $exp . " #"; 177 $flag = -1; 178 while (!(substr($exp,$flag + 1,1) == "#" && end($stackOps) == "#")){ 179 $str = substr($exp, $flag + 1, strpos($exp, " ", $flag + 1) - $flag -1); 180 if(!in_array($str,array("+","-","*","/","(",")","#"))){ 181 if(strpos($str,"/")){ 182 $numerator = substr($str,0,strpos($str,"/")); 183 $denominator = substr($str,strpos($str,"/") + 1); 184 array_push($stackNums,floatval($numerator)/floatval($denominator)); 185 $flag = strpos($exp," ",$flag + 1); 186 } else { 187 array_push($stackNums,floatval($str)); 188 $flag = strpos($exp," ",$flag + 1); 189 } 190 } else { 191 if($pri[end($stackOps)][$str] == "<"){ 192 array_push($stackOps,$str); 193 $flag = strpos($exp," ",$flag + 1); 194 }elseif ($pri[end($stackOps)][$str] == ">") { 195 $op = end($stackOps); 196 array_pop($stackOps); 197 $num2 = end($stackNums); 198 array_pop($stackNums); 199 $num1 = end($stackNums); 200 array_pop($stackNums); 201 array_push($stackNums, $this->calculate($num1, $num2, $op)); 202 }elseif ($pri[end($stackOps)][$str] == "=") { 203 array_pop($stackOps); 204 $flag = strpos($exp," ",$flag + 1); 205 } else { 206 echo "<script>alert('Push&Pop Error !!!');location='setattr.php';</script>"; 207 } 208 } 209 } 210 array_push($ansL, end($stackNums)); 211 } 212 return $ansL; 213 } 214 215 function getQuestionList(){ 216 return $this->items; 217 } 218 219 220 } 复制代码 基本思路和c++版的相同,不过php 数组的特性,还有字符串处理的优势,还是更方便一些,代码少了一半。 数据库连接用的是PDO,虽然比直接用mysql_* 的函数麻烦一点,但是安全性更高, 访问隔离(貌似是这么叫的)在每一部分都有,通过 session_start(); if(!isset($_SESSION['adminid']) && !isset($_SESSION['adminname']) ){ echo "<script>alert('请登录!');location='adminlogin.html';</script>"; } 或者 session_start(); if (!isset($_SESSION['adminid']) && !isset($_SESSION['adminname'])) { echo "<script>alert('请登录!');location='adminlogin.html';</script>"; } 实现,简单。 管理员部分对特定数据操作是在遍历中加入GET请求实现的 例如: 复制代码 <?php $stmt = $pdo->prepare("select * from ansrecord"); $stmt->execute(); $res = $stmt->fetchAll(PDO::FETCH_ASSOC); ?> <table border="1" align="center" width="40%" > <tr><th>序号</th><th>用户ID</th><th>题目</th><th>最后答案</th><th>操作</th></tr> <tr><td></td><td></td></tr> <?php foreach ($res as $key => $value) { echo "<tr><td>" . $key . "</td><td>" . $value['userid'] . "</td><td>" . $value['question'] . "</td><td>".$value['lastans']."</td><td><a href="ansrecordmangagement.php?action=DELETE&ansrecordid=".$value['ansrecordid']."" >删除记录</a></td></tr>"; } $stmt = null; $pdo = null; ?> </table>
从用户界面->OJ首页->OJ->题目属性设置->题目列表->提交题目和答案到数据库
题目属性设置页面提交一个POST表单,通过PHP输入过滤器对表单内容过滤,然后将属性做参数传入questionGenerator构造函数
$qG = new questionGenerator($itemNum, $isMulandDiv, $isParentheses, $isNeg, $isRem, $start, $end); $qG->generateQuest(); $qL = $qG->getQuestionList(); $numofqL = count($qL);
将题目通过只读的input框 显示,答案也用表单收集,题目数量通过一个隐藏域传入表单
<form method="post" action="submitquestandans.php"> <table> <tr><th>题目</th><th>答案</th></tr> <?php echo "<tr><td colspan="2"><input type="hidden" value="".$numofqL."" name="numofqL" > </td></tr>"; foreach ($qL as $key => $value) { echo "<tr><td><input type="text" name="question".$key."" value="".$value."" readonly="true" ></td><td><input type="text" name="answer".$key."" required size="10" ></td></tr>"; } ?> <tr><td colspan="2"><input type="submit" name="qL_submit" value="提交" ></td></tr> </table> </form>
我的队友信1301-2班 肖兴堂