Skip to content

使用event对象实现Ctrl+Shift键提交表单

本次实例采用phpwind8.0作为测试载体,首先对event对象的概念做一些必要的阐述:
event对象以属性的方式提供事件行为的相关信息,只要event对象可用,这些信息便能够被获取,比如以下列出了event对象的属性描述:

    • srcElement:触发事件的元素
    • type:事件类型
    • screenX:屏幕的x坐标
    • screenY:屏幕的y坐标
    • button:鼠标按键
    • keyCode:键盘按键
    • shiftKey:按下shift键
    • altKey:按下alt键
    • ctrlKey:按下ctrl键

      那接下来就对shiftKey和ctrlKey做一下简单的应用吧,具体表现为当用户按下Ctrl+Shift键,就可以实现提交表单,并将结果显示出来!

      首先,在phpwind8论坛根目录/tempalte/wind目录下创建一个htm模版文件,并命名为:test.htm,代码如下:

      <!--<?php print<<<EOT
      -->
      <form method="post" action="test.php" name="form">
      <span>标题:</span><input type="text" name="tbTitle"/>
      <br />
      <span>内容:</span><textarea id="taContent" name="taContent" cols="20" rows="10"></textarea>
      <br />
      <input type="submit" value="提交"/>
      </form>
      <script language="JavaScript">
      <!--
      var txtContent = document.getElementById("taContent");
      txtContent.onkeydown = function(evt){
      evt = evt ? evt : window.event;
      if(evt.ctrlKey && evt.shiftKey) this.form.submit();
      }
      //-->
      </script>
      <!--
      EOT;
      ?>-->

      然后,在phpwind8论坛根目录创建一个php文件,并命名为:test.php,代码如下:

      < ?php
      require_once 'global.php';
      
      initGP(array('tbTitle','taContent'));//接收表单post提交过来的两个变量
      echo $tbTitle."<br>".$taContent;//打印到浏览器
      require_once(R_P.'require/header.php');//调用论坛头部文件
      require_once(PrintEot('test'));//调用模版进行显示
      footer();//调用底部文件
      ?>

      onkeydown是文本框的一个键盘事件,定义了一个匿名函数作为事件处理程序,这个函数接收了一个参数evt,在非 IE的浏览器中,会自动将event对象作为参数传进来。而在IE中,event对象是一个全局的对象,在任何地方均可使用,并不会作为函数的参数,因此 需要使用如下语句来处理浏览器的兼容性:

      evt = evt ? evt : window.event;
      

      接下来就是打开http://localhost/pw8/test.php文件,提交表单内容,按下Ctrl+Shift键便可实现提交表单内容,并将提交的变量打印了出来,您不妨试试这个神奇的功能吧~~

      在线统计造成锁表严重的解决方法

      论坛的两种在线算法优先选择数据库算法,文本算法的效率是比较低的。

      论坛默认的sql_ol函数的删除操作过于频繁,导致长时间锁表的情况出现。针对这个情况,我们对这个函数的删除和更新操作逻辑进行了优化。

      表结构调整如下:

      ALTER TABLE `pw_online` CHANGE `ip` `ip` CHAR( 15 ) NOT NULL default '';
      ALTER TABLE `pw_online` CHANGE `action` `action` CHAR( 10 ) NOT NULL default '';
      ALTER TABLE `pw_online` ADD INDEX (`lastvisit`);
      

      函数Sql_ol(global.php)优化如下:

      function Sql_ol(){
      	global $db,$fid,$tid,$timestamp,$windid,$winduid,$onlineip,$groupid,$wind_in,$db_onlinetime,
                         $db_ipstates,$db_today,$lastvisit;
      	$olid = (int)GetCookie('olid');
      	$ifhide = $GLOBALS['_G']['allowhide'] && GetCookie('hideid') ? 1 : 0;
      	$isModify = 0;
      	PwNewDB();
      	$_groupid = $groupid;
      	$_groupid == 'guest' && $_groupid = 0;
      	$winduid = intval($winduid);
      	if ($olid) {
      		$sqladd = $winduid ? ('uid = '.pwEscape($winduid).' AND olid = '.pwEscape($olid)) : ('olid = '.pwEscape($olid).' AND ip = '.pwEscape($onlineip));
      		$pwSQL = pwSqlSingle(array(
      			'username' => $windid,
      			'lastvisit' => $timestamp,
      			'fid' => $fid,
      			'tid' => $tid,
      			'groupid' => $_groupid,
      			'action' => $wind_in,
      			'ifhide' => $ifhide,
      			'uid' => $winduid,
      			'ip' => $onlineip
      		));
      		$db->update("UPDATE pw_online SET $pwSQL WHERE $sqladd");
      		if ($winduid && !$db->affected_rows())
      		{
      			$db->update('DELETE FROM pw_online WHERE uid = '.pwEscape($winduid).' OR olid = '.pwEscape($olid));
      		}
      	} elseif (!$_COOKIE) {
      		$pwSQL = pwSqlSingle(array(
      			'username' => $windid,
      			'lastvisit' => $timestamp,
      			'fid' => $fid,
      			'tid' => $tid,
      			'groupid' => $_groupid,
      			'action' => $wind_in,
      			'ifhide' => $ifhide,
      			'uid' => $winduid
      		));
      		$db->update("UPDATE pw_online SET $pwSQL WHERE ip=".pwEscape($onlineip));
      	}
      	if (!$olid && $_COOKIE || !$db->affected_rows()) {
      		$_ms = get_date($timestamp, 'i-s');
      		list($_m, $_s) = explode('-', $_ms);
      		$_rm = $_m % 10;
      		$_rs = $_s % 10;
      		if (!$_rm && !$_rs)
      		{
      			$db->update('DELETE FROM pw_online WHERE lastvisit < '.pwEscape($timestamp-$db_onlinetime));
      		}
      		$rt = $db->get_one("SELECT MAX(olid) FROM pw_online", MYSQL_NUM);
      		$olid = ++$rt[0];
      		$db->update("REPLACE INTO pw_online (olid, username, lastvisit, ip, fid, tid, groupid, action, ifhide, uid)
                                             VALUES ('$olid', '".addslashes($windid)."', '$timestamp', '".addslashes($onlineip)."', '$fid',
                                             '$tid', '$_groupid', '$wind_in', '$ifhide', '$winduid')");
      		Cookie('olid',$olid);
      		$isModify = 1;
      	}
      	$ipscookie = GetCookie('ipstate');
      	if ($db_ipstates && ((!$ipscookie && $isModify===1) || ($ipscookie && $ipscookie<$GLOBALS['tdtime']))) {
      		require_once(R_P.'require/ipstates.php');
      	}
      	if ($db_today && $timestamp-$lastvisit>$db_onlinetime) {
      		require_once(R_P.'require/today.php');
      	}
      }
      

      对首页获取在线会员的sql语句(index.php)优化如下:

      $query = $db->query("SELECT COUNT( * ) AS count FROM pw_online WHERE uid = 0 UNION ALL SELECT COUNT( * ) AS count FROM pw_online");
      $i = 1;
      while ($rt = $db->fetch_array($query))
      {
      	$i++ == 1 ? ($guestinbbs = intval($rt['count'])) : ($userinbbs = intval($rt['count']));
      }
      unset($i);
      $userinbbs -= $guestinbbs;
      

      [phpwind高负载实战系列二]关于mysql数据分表建立sphinx全文索引的探讨

      最近在做XX项目(国内某著名社区)中遇到一个问题:他们的帖子表tmsgs分成了7个表,那么用sphinx建立全文索引的时候,就纠结了,怎么办呢?

      现在我们官方站点上使用的分表是采用让用户来选择回复分表的方式来解决这个问题:

      显然这种方式用户体验是极其糟糕的,会员才不知道也不关心他的帖子id是多少,回复id是多少呢?那么他要找出他想要的结果,必须得每个回复表去试,试完了还没办法显示到一起.估计会员对这个功能可能也有点意见。

      有一种方案是在sphinx配置文件中为每个分表生成配置一个索引,并生成7个不同的索引,增量索引也类似。这种方案虽然也可以达到目的,但是这种方案的烦琐程序可见一斑。相当于得在配置文件中生成14份关于pw_tmsgs及分表的配置(7份主索引和7个增量索引)。最后用合并主索引和增量索引的方式将所有索引合并到一个大的索引中来。 继续阅读’[phpwind高负载实战系列二]关于mysql数据分表建立sphinx全文索引的探讨’ »

      [phpwind高负载实战系列一]性能测试工具之XDEBUG 和WinCacheGrind

      我们团队在实战工作中经常会遇到一系列高负载相关的问题。随着经验的积累,在这方面也做了很多实战问题的沉淀。知识这东西藏着掖着也会发臭的,所以这次就计划整理成一个系列分享给所有对phpwind或者对php感兴趣的同学。希望有想法和建议的同学能够和我们一块儿交流,以及指点。

      第一篇由我们的王MM(西瓜)主导的性能测试工具相关的分享:

      性能测试工具之XDEBUG 和WinCacheGrind

      by 西瓜

      今天我对XX站(国内某大型社区)在本地做了一个性能测试,分析三大页面有没有性能瓶颈,主要用了PHP的XDEBUG 扩展和WinCacheGrind这个工具,主要方法和大家分享下,希望对大家在代码的性能上有所帮助。

      首先:下载PHP的XDebug扩展,网址:http://xdebug.org/download.php 下载符合自己的环境的扩展

      如果你的系统是WINDOWS,需要下载符合自己环境的DLL文件。

      下载下来放在PHP的ext目录,去掉后面Xdebug的 版本编号直接改成php_xdebug.dll

      然后修改php.ini,在文件的最后加上Xdebug配置信息

      [Xdebug]
      extension=php_xdebug.dll
      xdebug.auto_trace=on
      xdebug.collect_params=on
      xdebug.collect_return=on
      xdebug.profiler_enable=on
      xdebug.trace_output_dir="E:\APMServ5.2.0\Temp\xdebug" 
      //设定函数调用检测信息的输出文件路径,这个目录需要手动建立。
      
      xdebug.profiler_output_dir="E:\APMServ5.2.0\Temp\xdebug"
      //设定效能检测信息输出文件的路径,一般和上面的设成一样。
      

      然后重启APACHE,会看到PHPINFO里面有:

      (猛击看大图)

      说明安装成功了!!!

      之后你访问页面都会在E:\APMServ5.2.0\Temp\xdebug 目录下生成cachegrind.out的文件,需要有一个工具对这样的文件进行解析,就是WinCacheGrind,下载地址是:http://sourceforge.net/projects/wincachegrind/

      下载下来后运行,把cachegrind.out 文件拖到这个工具里面,

      就会出现这个页面所调用到的所有的函数以及函数用了多长时间。以thread.php为 例:

      (猛击看大图)

      这个可以看出thread.php调用了哪些文件,哪些函数,都用了多长时间,这样可以找出这个页面哪里最消耗时间,可以优化程序的性能。

      比如说global.php 总共消耗了580MS, 再点进global.php 就会出现

      (猛击看大图)

      这里面可以看出user_info这个函数用了115ms,一 直这么下去,可以找到影响性能的函数和原因。

      我只是在本地(windows系统)做了这样的测试,如果需要在LINUX服务器上做测试的,可以通过这个方法,如下:

      在Linux下编译安装XDebug

      tar -xzf xdebug-2.0.0RC3.gz
      cd xdebug-2.0.0RC3
      /usr/local/php/bin/phpize
      ./configure --enable-xdebug
      cp modules/xdebug.so /usr/local/php/lib/php/extensions/no-debug-non-zts-20020429/
      

      注:/usr/local/php/lib/php/extensions/no-debug-non-zts-20020429/不 同的PHP版本路径不同,也不一定要放在该路径,可以在zend_extension_ts中自行指定xdebug.so所在位置。

      修改php.ini,去除PHP加速模块,增加以下配置信息支持XDebug扩展

      [Xdebug]
      
      zend_extension_ts="/usr/local/php/lib/php/extensions/no-debug-non-zts-20020429/xdebug.so"
      
      xdebug.profiler_enable=on
      
      xdebug.trace_output_dir="/tmp/xdebug"
      
      xdebug.profiler_output_dir="/tmp/xdebug"
      
      xdebug.profiler_output_name="script"
      
      mkdir -p /tmp/xdebug
      chmod 755 /tmp/xdebug
      chown www:www /tmp/xdebug
      /usr/local/apache/bin/apachectl -k restart
      

      重启APACHE服务器后,看xdebug扩展有没有装上,之后还是用WinCacheGrind这个工具来看

      OVER,OVER了!哈哈,希望这个对大家有所帮助!!!能够更好的找出我们写的代码哪里会有性能瓶颈。

      某二次开发项目总结

      XX项目总结

      代码质量

      在没有应用场景的情况下讲代码质量是很空洞的。考虑到自己的技术掌握情况、项目工作的性质,对目前的代码质量作一下总结性的记录。

      XX项目的类型是二次开发,主要是功能上的增删改,以及html2php的套模板工作。做项目和做产品不一样,不管是在需求上、着重点还是资源分配上。就目前项目而言,代码质量上关注点的重要性从高到低大概就是sql性能问题、安全性问题、代码的清晰度、模块的可扩展性、php代码的性能等。相较于产品类开发,项目的资源投入上相对比较紧张,满足当前需求、控制好代码本身的bug率基本可以了,如果能够做到模块的可扩展性,减少不必要的代码冗余,以及代码逻辑处理的优雅就更好了。做二次开发在很多情况下都是一个权衡的工作,资源和代码质量是一个权衡,模块的独立性和代码冗余也是个权衡。在做二次开发时,应该尽量避免动基础核心代码,尽量在相对较外围的代码中增加(冗余)代码,并且做好一定的注释工作。作为新人,特别要注意有sql查询的部分,开发完sql语句多的模块可以找有经验的同事review下。安全性的问题如果没有相关安全经验的话在几个需要处理的关键部位做好基本的过滤处理就可以了。

      薄弱点

      一个项目下来,也基本清楚在这方面的技术上的薄弱点在哪里,那些地方需要改进。就我个人而言,js(尤其是Ajax)方面比较薄弱,在处理相关问题时,就有些棘手。对于pw自身的JS核心不是很了解,在修改js组件核心代码时,显得比较吃力。

      此外,对于代码的质量做得不够到位。在做完一个功能点后,没有进行细致的自测,导致后续bug频繁的出现。接下来,开始谈谈关于二次开发的一些看法。

      二次开发的窘境

      在常规项目中,对于开发人员来说,PM或PD(或RA)的需求排在第一位。PD角色的存在就是来应对开发直接面对客户需求时的困境。在二次开发中,开发人员需要兼任PD、Dev、QA,以及部分PM的职能,需要直接面对客户需求。

      客户的需求为什么这么难以应付?有什么有效的方法去提高沟通、以及项目整体上的效率呢?

      稍微过于极端地角度上去看,客户在一定程度上来讲是非理性人,他们的有些需求往往会自相矛盾。另外,他们不会从项目开发的成本上去考虑。并不是说客户是如此地不讲道理,或者说如何地不懂技术,只是跨公司、跨团队、跨职能的合作本身就容易暴露问题。

      对于上述问题自然而然会想到一些应对方法,比如:对客户的需求尽量提出自己作为开发的建议以及修改方案;在需求了解阶段尽量确认好需求细节;评估好需求成本,尽量做好项目计划等等。我不否认上述方法都是非常不错的,但它们只是一些软性的且并不是一针见血的笼统方案,这也是我一刻不停地用“尽量”去修饰的原因。俗称非理性,也是需求不容易确认、需求不容易建议、计划不容易做好的原因所在。

      为什么需求不容易确认?因为客户的需求是在变的,他们不会像PD那样会将需求方案做到多么细致和完善。他们的第一份需求说明书往往是根据之前站点程序的功能点、UI、逻辑给出的第一印象的说明书。另外,他们还会有一种观念,重于需求功能点的量,而不是用户体验。应对的方法不应该是那些在项目已经进行的时候才开始投入的被动方法,很多有效地方案都是在前期计划时就开始运作。相对来说,主动地进攻会比被动地防御来得更为高效!之前老柯提到的将项目分为一期、二期的计划,在项目开始前期就和客户约定好,并做好相关安排。保证客户的需求不会做过大的改变,至少在一期项目的时候不会有很大的变动。此外,在了解需求阶段,除了熟悉需求说明书,更不要忘了去熟悉真实环境,也就是原始站的功能点。去使用原始站的功能点才是了解需求的最直接方法。需求说明书只是客户用来转述其需求想法的中间代理,它会在某种程度上过滤掉很多关键信息。对于一些复杂的功能点,暂时无法想到很好的有效方法。有时候当功能点有些复杂的时候,客户根本不知道它应该是什么样子的,操作流程应该如何,内部逻辑应该如何运作等等。实际的项目过程中往往是等开发做出该功能点的demo,或者一部分实现,客户再根据这些进一步地提改进需求。

      为什么需求不容易建议?首先客户会站在他自己的立场去思考,他会将自己的需求实现放在第一位,不会考虑任何关于项目开发成本的问题。从某种角度上看,他会对你在技术上“偷懒”产生不好的评价。众所周知,程序员都应该学会在技术上“偷懒”,这样才能提升开发效率。尤其是二次开发,代码复用比例特别高的开发方式。所以,在对客户需求进行建议的时候尤其要避免让他产生疑虑的那些点。因为经验有限,我无法在这里描述清楚这些点。但是可以肯定的是,假如在一开始你不小心让客户产生过多的疑虑,在之后的项目合作中,会更加被动。

      为什么计划不容易做好?这里的计划主要还是针对于二次开发的计划,因为二次开发的性质,所以在代码上能够复用原程序框架上的代码就必须复用,即使是复用成本比重写成本更高也尽量复用。这样可以保证程序整体上的逻辑不增加过多的复杂性,减少bug产生,以及减轻今后程序升级的压力。为什么会产生复用成本比重写成本更高呢?目前的程序在很多地方还不是很成熟,导致复用的时候不能够尽兴。至此,二次开发中对一个功能点的计划评估时,新写功能点代码、复用功能模块、部分复用、重写模块等之间的成本相差十很大的,这就导致计划很难做到位。为了做好计划,自己本身上需要对程序本身的代码框架足够的熟悉。假如在某些功能点上无法确定,可以咨询TOP或云天河这样有经验的老同事。另外需要补充的是,很多版本历史上的问题是无法通过代码阅读直接获得的。

      上述对于XX项目的总结很多地方过于扯淡,请各位有经验的前辈们多多拍砖!