Xunsearch 整合到Yii2框架中

公司一直用的YII2框架,然后要做一个中文搜索引擎,所有想的Xunsearch这个项目,之前有文章提到了,怎么安装Xunsearch服务端。Xunsearch项目安装

xunsearch暂时只有Liunx的服务端和PHP-SDK。但作者确提供了一个YII2的扩展。突然感觉好幸福的说。

本来是通过使用PHP-SDK写一个简单的添加索引和更新索引还有删除索引的功能,但有考虑到多项目的情况。就是一个Xunsearch可以被多个项目公用,所有配置文件放置在每一个项目的配置文件里面,根据这个项目的配置文件来区分每一个搜索数据库。这是我一开始的想法,但重点是不是怎么想,而是怎么去做。实践才是硬道理,然后就遇到下面的这些问题。

http://www.yiiframework.com/extension/yii-xunsearch/#hh4 这里目标链接,我接下来要说下安装心得

[php]
// application components
‘components => [
// … other components …
‘xunsearch’ => [
‘class’ => ‘hightman\xunsearch\Connection’, // 此行必须,这里不一定是这写的,可能是hightman.xunsearch.Connection 根据实际项目出发
‘iniDirectory’ => ‘@app/config’, // 搜索 ini 文件目录,默认:@vendor/hightman/xunsearch/app
‘charset’ => ‘utf-8’, // 指定项目使用的默认编码,默认即时 utf-8,可不指定
],
],
[/php]

然后,如果你的PHP环境是集成的安装包,可能会遇到这么一个问题

[php]
Unexpected character in input: ‘\’ (ASCII=92) state=1
[/php]

网上有人说是 php.ini 里面的配置问题,说把,short_open_tag 改成 On,我第一看感觉很有道理,然后找到发现,本来就是On。

然后到http://stackoverflow.com(全世界的技术都在用的一个技术论坛,官方语言是英语)上面去找,果然找到了。知道真相的我,真的是不想多了。

因为那个YII2的扩展用到了PHP5.3才有的命名空间特效。而我的本地PHP版本是5.2.6。我表示淡定不了。折磨一天的事情,居然是因为PHP版本太低。

就总结到这里,我现在只想静静。

将HTML页面转行成word并且保存

这里用使用到一个PHP的工具叫:PHPWord。

生成Word的原理是,将堆规定好了的xml压缩成一个zip包,并且把后缀名改成doc或者docx即可。

所以使用PHPWord,需要你的PHP环境安装zip.dll压缩扩展,我写了一个demo.

Deom演示:http://www.dengwz.com/phpword/index.html 往里面贴一个URL就可以了

功能说明:

20150507 — HTML中的<p>标签和<ol>列表标签的获取

20150508 — 新增获取文章中的图片功能

20150509 — 新增行间距,并且过滤一下错误图片

20150514 — 新增表格处理,并且将代码改成面向对象

20150519 — 新增GD库处理网络图片

[php]

&amp;nbsp;require_once 'PHPWord.php';
 require_once 'SimpleHtmlDom.class.php';

 class Word{

 private $url;
 private $LinetextArr = array();
 public $CurrentDir;
 public $error = array(); //错误数组
 public $filename = null;
 public $Allowtag = "p,ol,ul,table";

 /**数据统计**/
 public $DownImg = 0;
 public $expendTime = 0;
 public $HttpRequestTime = 0;
 public $ContentLen = 0;
 public $HttpRequestArr = array();
 public $expendmemory = 0;

 public function __construct($url)
 {
 $startTime = $this-&amp;gt;_Time();
 $startMemory = $this-&amp;gt;_memory();

 $this-&amp;gt;url = $url;

 $UrlArr = parse_url($this-&amp;gt;url);

 $this-&amp;gt;host = $UrlArr["scheme"]."://".$UrlArr['host'];

 $this-&amp;gt;CurrentDir = getcwd();

 $this-&amp;gt;LinetextArr["table"] = array();

 $html = new simple_html_dom($this-&amp;gt;url);

 $this-&amp;gt;HttpRequestArr[] = $this-&amp;gt;url;
 $this-&amp;gt;HttpRequestTime++;

 foreach($html-&amp;gt;find($this-&amp;gt;Allowtag) as $key=&amp;gt;$value)
 {
 if($value-&amp;gt;tag == "table")
 {
 $this-&amp;gt;ParseTable($value,0,$this-&amp;gt;LinetextArr["table"]);
 }
 else
 {
 $this-&amp;gt;AnalysisHtmlDom($value);
 }

 $this-&amp;gt;error[] = error_get_last();

 }

 $endTime = $this-&amp;gt;_Time();
 $endMemory = $this-&amp;gt;_memory();

 $this-&amp;gt;expendTime = round(($endTime-$startTime),2); //微秒
 $this-&amp;gt;expendmemory = round(($endMemory-$startMemory)/1000,2); //bytes

 $this-&amp;gt;CreateWordDom();

 }

 private function _Time()
 {
 return array_sum(explode(" ", microtime()));
 }

 private function _memory()
 {
 return memory_get_usage();
 }

 /**
 * 解析HTML中的Table,这里考虑到多层table嵌套的情况
 * @param $value HTMLDOM
 * @param $i 遍历层级
 * **/
 private function ParseTable($value,$i,$Arr)
 {
 if($value-&amp;gt;firstChild() &amp;amp;&amp;amp; in_array($value-&amp;gt;firstChild()-&amp;gt;tag,array("table","tbody","thead","tfoot","tr")))
 {
 foreach($value-&amp;gt;children as $k=&amp;gt;$v)
 {
 $this-&amp;gt;ParseTable($v,$i++,$Arr);
 }
 }
 else
 {
 foreach($value-&amp;gt;children as $k=&amp;gt;$v)
 {

 if($v-&amp;gt;firstChild() &amp;amp;&amp;amp; $v-&amp;gt;firstChild()-&amp;gt;tag != "table")
 {
 $Arr[$i][] = array("tag"=&amp;gt;$v-&amp;gt;tag,"text"=&amp;gt;trim($v-&amp;gt;plaintext));
 }

 if(!$v-&amp;gt;firstChild())
 {
 $Arr[$i][] = array("tag"=&amp;gt;$v-&amp;gt;tag,"text"=&amp;gt;trim($v-&amp;gt;plaintext));
 }
 }

 }
 }

 /**
 * 解析HTML里面的表情
 * @param $value HTMLDOM
 * **/
 private function AnalysisHtmlDom($value)
 {
 $tmp = array();
 if($value-&amp;gt;has_child())
 {
 foreach($value-&amp;gt;children as $k=&amp;gt;$v)
 {
 $this-&amp;gt;AnalysisHtmlDom($v);
 }
 }
 else
 {
 if($value-&amp;gt;tag == "a")
 {
 $tmp = array("tag"=&amp;gt;$value-&amp;gt;tag,"href"=&amp;gt;$value-&amp;gt;href,"text"=&amp;gt;$value-&amp;gt;innertext);
 }
 else if($value-&amp;gt;tag == "img")
 {
 $src = $this-&amp;gt;unescape($value-&amp;gt;src);
 $UrlArr = parse_url($src);

 if(!isset($UrlArr['host']))
 {
 $src = $this-&amp;gt;host.$value-&amp;gt;src;
 $UrlArr = parse_url($src);
 }

 $src = $this-&amp;gt;getImageFromNet($src,$UrlArr); //表示有网络图片,需要下载

 if($src)
 {
&amp;nbsp;  $imgsArr = $this-&amp;gt;GD($src);
   $tmp = array("tag"=&amp;gt;$value-&amp;gt;tag,"src"=&amp;gt;$src,"text"=&amp;gt;$value-&amp;gt;alt,"width"=&amp;gt;$imgsArr['width'],"height"=&amp;gt;$imgsArr['height']); }
 }
 else
 {
 $tmp = array("tag"=&amp;gt;$value-&amp;gt;tag,"text"=&amp;gt;strip_tags($value-&amp;gt;innertext));
 } 

 $this-&amp;gt;LinetextArr[] = $tmp;
 }

 }

 /**
 * 根据GD库来获取图片的如果太多,进行比例压缩
 * **/
 private function GD($src)
 {
 list($width, $height, $type, $attr) = getimagesize($src);

 if($width &amp;gt; 800 || $height &amp;gt; 800 )
 {
 $width = $width/2;
 $height = $height/2;
 }

 return array("width"=&amp;gt;$width,"height"=&amp;gt;$height);
 }

 /**
 * 将Uincode编码转移回原来的字符
 * **/
 public function unescape($str) {

 $str = rawurldecode($str);

 preg_match_all("/(?:%u.{4})|&amp;amp;#x.{4};|&amp;amp;#\d+;|.+/U",$str,$r);

 $ar = $r[0];

 foreach($ar as $k=&amp;gt;$v) {

 if(substr($v,0,2) == "%u"){

 $ar[$k] = iconv("UCS-2BE","UTF-8",pack("H4",substr($v,-4)));
 }

 elseif(substr($v,0,3) == "&amp;amp;#x"){

 $ar[$k] = iconv("UCS-2BE","UTF-8",pack("H4",substr($v,3,-1)));

 }
 elseif(substr($v,0,2) == "&amp;amp;#"){

 $ar[$k] = iconv("UCS-2BE","UTF-8",pack("n",substr($v,2,-1)));
 }
 }

 return join("",$ar);

}

 /**
 * 图片下载
 * @param $Src 目标资源
 * @param $UrlArr 目标URL对应的数组
 * **/
 private function getImageFromNet($Src,$UrlArr)
 {
 $file = basename($UrlArr['path']);

 $ext = explode('.',$file);

 $this-&amp;gt;ImgDir = $this-&amp;gt;CurrentDir."/".$UrlArr['host'];

 $_supportedImageTypes = array('jpg', 'jpeg', 'gif', 'png', 'bmp', 'tif', 'tiff');

 if(isset($ext['1']) &amp;amp;&amp;amp; in_array($ext['1'],$_supportedImageTypes))
 {
 $file = file_get_contents($Src);

 $this-&amp;gt;HttpRequestArr[] = $Src;
 $this-&amp;gt;HttpRequestTime++;

 $this-&amp;gt;_mkdir(); //创建目录,或者收集错误

 $imgName = md5($UrlArr['path']).".".$ext['1'];

 file_put_contents($this-&amp;gt;ImgDir."/".$imgName,$file);

 $this-&amp;gt;DownImg++;

 return $UrlArr['host']."/".$imgName;
 }

 return false;
 }

 /**
 * 创建目录
 * **/
 private function _mkdir()
 {

 if(!is_dir($this-&amp;gt;ImgDir))
 {
 if(!mkdir($this-&amp;gt;ImgDir,"7777"))
 {
 $this-&amp;gt;error[] = error_get_last();
 }
 }
 }

 /**
 * 构造WordDom
 * **/
 private function CreateWordDom()
 {

 $PHPWord = new PHPWord();

 $PHPWord-&amp;gt;setDefaultFontName('宋体');
 $PHPWord-&amp;gt;setDefaultFontSize("11");

 $styleTable = array('borderSize'=&amp;gt;6, 'borderColor'=&amp;gt;'006699', 'cellMargin'=&amp;gt;120);
 // New portrait section
 $section = $PHPWord-&amp;gt;createSection();

 $section-&amp;gt;addText($this-&amp;gt;Details(),array(),array('spacing'=&amp;gt;120));

 //数据进行处理
 foreach($this-&amp;gt;LinetextArr as $key=&amp;gt;$lineArr)
 {
 if(isset($lineArr['tag']))
 {
 if($lineArr['tag'] == "li")
 {
 $section-&amp;gt;addListItem($lineArr['text'],0,"","",array('spacing'=&amp;gt;120));
 }
 else if($lineArr['tag'] == "img")
 {
 $section-&amp;gt;addImage($lineArr['src'],array('width'=&amp;gt;$lineArr['width'], 'height'=&amp;gt;$lineArr['height'], 'align'=&amp;gt;'center'));
 }
 else if($lineArr['tag'] == "p")
 {
 $section-&amp;gt;addText($lineArr['text'],array(),array('spacing'=&amp;gt;120));
 }
 }
 else if($key == "table")
 {
 $PHPWord-&amp;gt;addTableStyle('myOwnTableStyle', $styleTable);
 $table = $section-&amp;gt;addTable("myOwnTableStyle");
 foreach($lineArr as $key=&amp;gt;$tr)
 {
 $table-&amp;gt;addRow();
 foreach($tr as $ky=&amp;gt;$td)
 {
 $table-&amp;gt;addCell(2000)-&amp;gt;addText($td['text']);
 }
 }
 }
 }

 $this-&amp;gt;downFile($PHPWord);
 }

 public function Details()
 {
 $msg = "一共请求:{$this-&amp;gt;HttpRequestTime}次,共下载的图片有{$this-&amp;gt;DownImg}张,并且下载完成大约使用时间:{$this-&amp;gt;expendTime}秒,整个程序执行大约消耗内存是:{$this-&amp;gt;expendmemory}KB,";

 return $msg;
 }

 public function downFile($PHPWord)
 {
 if(empty($this-&amp;gt;filename))
 {
 $UrlArr = parse_url($this-&amp;gt;url);
 $this-&amp;gt;filename = $UrlArr['host'].".docx";
 }

 // Save File
 $objWriter = PHPWord_IOFactory::createWriter($PHPWord, 'Word2007');
 $objWriter-&amp;gt;save($this-&amp;gt;filename);

 header("Pragma: public");
 header("Expires: 0");
 header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
 header("Cache-Control: public");
 header("Content-Description: File Transfer");

 //Use the switch-generated Content-Type
 header('Content-type: application/msword');//输出的类型

 //Force the download
 $header="Content-Disposition: attachment; filename=".$this-&amp;gt;filename.";";
 header($header);
 @readfile($this-&amp;gt;filename);
 }
}

[/php]

上面的代码重点感觉不是word生成,而是Simplehtmldom的使用,这是一个开源的HTML解析器,之前有提到,这几天在看他的代码,

引出了两个学习方向

  1. 正在表达式
  2. 这个扩展的函数整理

看源代码的收获:

PHP的异常是可以捕获的,而且PHP的错误也是可以捕获的。

[php]

error_get_last() &amp;nbsp;//用这个函数可以捕获页面中的PHP错误,不谢。

[/php]

用PHP在innodb引擎下,快速代建全文搜索功能

需要准备的设备:Liunx(Centos)操作系统(只支持Linux),PHP环境。

这里介绍一个国人开发的搜索引擎开源项目—讯搜(xunsearch),它分为:索引服务器和搜索服务器。

在CentOS下面愉快的代建服务器

[php]

wget http://www.xunsearch.com/download/xunsearch-full-latest.tar.bz2

tar -xjf xunsearch-full-latest.tar.bz2

[/php]

准备安装了

cd xunsearch-full-1.3.0/ sh setup.sh      //这里可以能遇到安装失败的问题,是因为没有安装gcc gcc-c++ 这样的扩展。用yum 安装十分简单

安装的时候要你输入安装目录,一般是 /usr/local/xunsearch  回车然后再输入Y回车,安装有点漫长。。

等待安装完成之后

具体操作请参照这里 : http://www.iterror.cn/q-1.html

安装完成后,怎么跨服务器来访问:

[php]

安装目录(/usr/local/xunsearch/)+bin/xs-ctl.sh -b a.b.c.d start // 监听在指定 IP 上 使用这个,把监听绑定到指定的外网能访问的IP上面。我的是 192.168.1163.130
[/php]

还有需要使用iptables 来开放8383和8384端口。最简单的是关闭iptables.

======================================================

PHP-SDK 配置文件修改,如下修改

[php]

[配置文件]

project.name = demo

project.default_charset = utf-8

server.index = 192.168.116.130:8383  //索引服务器的地址,默认是8383

server.search = 192.168.116.130:8384 //搜索服务器的地址,默认是8384

[pid]

type = id

[subject]

type = title  //设置被索引字段

[message]

type = body   //设置被索引字段

[chrono]

type = numeric

[配置文件]

[/php]

====================================================

具体使用细节就请查考使用手册:http://www.xunsearch.com/doc/php/guide/index.overview

性能测试:

测试结果数据

读Mysql索引

最近一直在忙,只有晚上才有时间抽空看《高性能MYSQL》看完后收获挺多。

下面主要就两个方面进行探讨

  1. innodb 和 MYISAM 引擎的区别
  2. B-tree 和 哈希索引的区别

INNODBy引擎和B-TREE存储

之前一问innodb有什么特别?一说支持事务,支持乐观锁(行级锁),写入比查询快。然后就没了

这不能怪我,主要是我之前读书少,之前的状态就是:读书少,想的多(这是很多应届生通病,我正视)

上面的说的innodb的三个特点,我现在只信服两个,因为自从mysql5.0以后,innodb的读写速度差距已经不是那么大了。

因为这个innodb的存储引擎有关系。B-tree存储。

  1. B-TREE存储引擎对索引的是顺序存储。
  2. B-TREE的索引会包含被索引的列的数据(看懂到这个我瞬间明白了,select * from tablename 为会被不建议了,因为这样会使用不到索引覆盖(之前听得没听过的))

MYISAM引擎

之前一说:myisam引擎,之前的理解是:读的数据快,但不支持事务,支持悲观锁(表锁)

现在的理解是 Myisamz支持全文索引(是一种关键字提取),在没有where的条件下select count(*)速度块,因为它有一个AUTO_INCREMENT

myisam的存储方式是三个文件,这三个文件是跨平台的。如果存储在CD-ROM这类只读的介质上面,而且MYISAM支持压缩。它就有,空间小,速度快的特点。有这个特性,它立足MYSQL众多引擎中的一员是完全有地位的。

说到最后:使用什么引擎或者建立什么类型的索引都是看应用场景。在我现在的工作中80%的场景都是innodb。

但如果可以容忍数据错误,或者对数据的正确性不是很强烈的要求。那么myisam也是不错的选择。

在说说哈希索引:

哈希索引是顺序存储的,而哈希索引的存储比较紧凑,所有查找会比较快速。(这里只有memory引擎是用的哈希索引,mysql的临时表都是用的这个引擎,如果临时表太大,临时表就没被写到硬盘上,那时的引擎是MYISAM)

基于这个原因,索引插入的开销比较大,需要维护哈希表。这就是为什么读越快于写了。

最后在说两句:

然后再说一个,之前一直觉得很高大的上的东西,就是读写分离

如果做到读写分离,我之后会写一个博文专门说实现过程。这里先说原理:MYSQL的主从复制

MYSQL5.0以后支持了二进制同步。这样,就做一个数据库做主库专门用来写数据,然后另一个数据库做从库专门用来查数据。

Giga Image Viewer — 一个用jquery在图片上面描点的插件

最近还是做了这件事,,Giga Image Viewer 是一个很优秀的Jquery插件,作者并没有开源,可是JS代码是强制开源的

并且遵从GPL协议,最近做一个项目要用,很鄙视国内一下某些平台盗用别人代码还收费的现象。。

 

我就把代码都down下来,并且处理了一下。其实作者还是提供了很多方便的API,(业界良心呀)

[javascript]
$(document).ready(function(){
var settings = {
‘viewportWidth’ : ‘100%’,
‘viewportHeight’ : ‘100%’,
‘fitToViewportShortSide’ : true,
‘contentSizeOver100’ : false,
‘startScale’ : .5, //注意是.+数字
‘startX’ : 1860,
‘startY’ : 2830,
‘animTime’ : 500,
‘draggInertia’ : 10,
‘imgDir’ : ‘images/’, //图片的目录
‘mainImgWidth’ : 5000,
‘mainImgHeight’ : 5000,
‘intNavEnable’ : true,
‘intNavPos’ : ‘B’,
‘intNavAutoHide’ : false,
‘intNavMoveDownBtt’ : true,
‘intNavMoveUpBtt’ : true,
‘intNavMoveRightBtt’ : true,
‘intNavMoveLeftBtt’ : true,
‘intNavZoomBtt’ : true,
‘intNavUnzoomBtt’ : true,
‘intNavFitToViewportBtt’ : true,
‘intNavFullSizeBtt’ : true,
‘intNavBttSizeRation’ : 1,
‘mapEnable’ : true,
‘mapPos’ : ‘BL’,
‘popupShowAction’ : ‘click’,
‘testMode’ : false
};

$(‘#myDiv’).lhpGigaImgViewer(settings, ‘MyHotspots’);
});
[/javascript]

参数真心多,,还有一个必须的

[html]
<span class="comment"><!– viewer container –></span>
<div id="myDiv" style="width:960px; height:600px; overflow:hidden; border:solid 1px #a6a6a6; background:#000;"></div>

<span class="comment"><!– hotspots container –></span>
<div id="MyHotspots" style="display:none;">

<span class="comment"><!– hotspot –></span>
<div class="lhp_giv_hotspot" data-x="1597" data-y="2569" data-visible-scale=".4">
<span class="comment"><!– marker –></span>
<div class="lhp_giv_marker pos-BR">
<img src="gfx/arrow2.png" style="float:right;" />
<span class="label">Click me <span>#2</span></span>
</div>
<span class="comment"><!– pop-up –></span>
<div class="lhp_giv_popup pos-R">
<div class="content">
<span>Lorem ipsum #2</span><br/>
<img src="gfx/popupImg1.jpg" />
Lorem ipsum dolor sit amet, consectetur adipisicing elit…
</div>
</div>
</div>

<span class="comment"><!– hotspot –></span>

</div>
[/html]

这样的话,这要修改下面的HTML功能就可以在地图上面描点了,看一下代码你就懂怎么描点的,我就不多少了。

我现在在想,我要不要把整理后的代码PUSH的出来呢?犹豫中… 作者售价是10美元

湖南工业大学-校园助手!从开始到现在

找了一个Bootstrap管理系统的设计页面,然后就开始整理这几年来的一直在陆陆续续做的一个系统–校园助手。

主要实现的功能:成绩查询,课表功能,计算总平均分和总绩点。

工大圈 - 湖南工业大学最大的校内

这是管理系统的管理界面。(上面的数据是真实的!!可惜不是我的)

开始我只打算做了WEB版,然后有几个小兄弟说要过来帮忙做APP!然后马上马不停蹄就出了APP。所有人都是没经验的,都摸着石头过河

QQ图片20150301184154

这是是APP的展示页面。。 反正我挺喜欢的。

好了APP和WEB功能就先介绍到这个,现在主要开发的过程。

==== 华丽的分割线====

当时想做一个关于正方教务系统的扩展系统。那是大二的时候,因为饱受正方系统抢课的摧残。

可惜:当时年轻,只有想法,没有能力。当时拦着的就是验证码的问题!远程提交表单。

机遇:当年在酷狗音乐实习,要做一个自动化测试工具,就是用CURL去请求URL并且读取CSV文件去调用接口。

结果:学会了CURL远程登录原理。这里在之前的文章中有提到。

但时间已经到了大四,但是是通过手动输入验证码的方式登录教务系统。

但需求远远不能被满足,因为之前是想法是,只要在这里登录一次,在不修改密码的前提下,要最快更新成绩。

这样时间到了毕业季,偶尔看到一个也是正方教务系统的学校做的微信公共帐号,这样!给一直想绕过验证码的我提供了思路。

然后,就有你看到的这个系统上线。

=====又是华丽的分割线======

20150228 离预计发布APP的日子还有一天!在连续奋斗十几个小时后,终于联调完成。发布了APP 0.1版。

该APP只有三个功能:成绩查询(统计暂时未通过的课程,平均分,绩点和历年成绩)。四六级成绩,校历

WEB端也新增了定时采集列队。通过主从MYSQL,将数据的读写分离,用Redis的消息列队来采集成绩。

说说这十几个小时,满满都是正能量。当时小哥说,这逻辑都知道了,12点之前应该能发布!只要联调好API.

但当联调好API的时候,就应该凌晨1点了,安卓数据展示界面还没写好。(扯淡一般的预估,这里主要的问题还在我,我对API的文档书写的不规范,造成了很多的沟通,增加了开发成本)

然后时间一秒一秒的过去,实在是扛不住了,我们已经看到广州凌晨5点的天色。

后记

因为还没有做压力测试和服务端中间件来均衡流量。不敢马上发布出去,先拉内测用户。

整整两年了,说不出的感觉,反正幸好没有放弃,这里谢谢几个小哥的支持。才能盼到这个APP上线。

虽然这个APP也好,WEB端也好,没有办法盈利,但它是我大学的一个梦想。我就是要做它,而且要做好它。

递归调用,POST循环请求

jQuery 代码部分

[javascript]

$(function(){

       _post(‘demo.php’,{userid:’PHP’});
 });

    function _post(url,post){

                   $.post(url,post,function(data){

                         if(data.status){
                                   _post(data.back.url,data.back.data);
                      }

                 },"json");
 }

[/javascript]

PHP代码

[php]

$data = array(‘back’=>array(‘url’=>’demo2.php’,’data’=>array(‘userid’=>’dwz’)),’status’=>true);
echo json_encode($data);

[/php]

效果页面

PHP开发工程师

PHPexecl操作手册–生成复杂的报表表头类

以前一直有需求,能把Execl里面的数据导入数据库,并且把数据库里面的数据导出到Execl中。

[php]

require_once dirname(__FILE__) . ‘/../Classes/PHPExcel/IOFactory.php’;

class PHPExeclCore extends PHPExcel_IOFactory{

public static function SummerCreateExecl($Head,$data)
{
self::SummerCreateExeclHead($Head,$data,"Excel2007");
}

public static function SummerReadExecl($dir)
{
if(!file_exists($dir))
{
echo "Execl Not Exist";
}
else
{
$PHPExeclObj = self::load($dir);

$sheetCount = $PHPExeclObj->getSheetCount(); //得到Execl中包含的Sheet工作簿的数量

for($i=0;$i<$sheetCount;$i++)
{
$ActiveSheet = $PHPExeclObj->getSheet($i);
$highestRow = $ActiveSheet->getHighestRow(); // 取得总列数
$allColumn = $ActiveSheet->getHighestColumn();

//通过嵌套循环来读取sheet工作簿里面的内容
for($Col=’A’;$Col<$allColumn;$Col++)
{
for($Row=1;$Row<$highestRow;$Row++)
{
$Data[$Col][$Row] = $ActiveSheet->getCell($Col.$Row)->getValue();
}
}

}
}

return $Data;

}

/*
* 将数据写入到数据表中
* $Data Array 表示要插入进Execl数据
* $RuleData Array 表示数据格式的规则数组
* $i int 表示从第几行起的插入数据
* **/

public static function SummerInsertDateToExecl($sheet,$Head,$Data,$n=3,$RuleData=array())
{

$SimpleHead = self::getHead($Head);

$row = $n;
foreach($Data as $key=>$valueArr)
{
$m = 0;
foreach($valueArr as $k=>$v)
{
$StartCol = PHPExcel_Cell::stringFromColumnIndex($m).$row;
$sheet->getCell($StartCol)->setValue($v);

$sheet->getStyle($StartCol)->getAlignment()->applyFromArray(
array(
‘horizontal’=> PHPExcel_Style_Alignment::HORIZONTAL_CENTER,
‘vertical’ => PHPExcel_Style_Alignment::VERTICAL_CENTER,
‘rotation’ => 0,
‘wrap’ => TRUE,
)
);

if(isset($SimpleHead[$k][‘col’]))
{
$m = $m + $SimpleHead[$k][‘col’]-1;
$endCol = PHPExcel_Cell::stringFromColumnIndex($m).$row;
$sheet->mergeCells($StartCol.":".$endCol);
}

$m++;

$type = false;

if(isset($SimpleHead[$k][‘type’]))
{
$type = $SimpleHead[$k][‘type’];
$AllowArray = $SimpleHead[$k][‘allowarray’];
}

//设置单元格的数据验证
if($type)
{
switch ($type)
{
case ‘list’:
self::setSelectionRange($sheet, $StartCol,$AllowArray);
break;
case ‘range’:
self::setValueRange($sheet, $StartCol,$AllowArray);
break;
}
}

}
$row ++ ;
}

}

/*
* 生成Execl单元格备注
* $sheet 当前的工作簿对象
* $Cell 需要设置属性的单元格
* $content 备注内容
* */
private static function setComment($sheet,$Cell,$content)
{
$sheet->getComment($Cell)->setAuthor(‘4399om’);
$objCommentRichText = $sheet->getComment($Cell)->getText()->createTextRun(‘4399om:’);
$objCommentRichText->getFont()->setBold(true);
$sheet->getComment($Cell)->getText()->createTextRun("\r\n");
$sheet->getComment($Cell)->getText()->createTextRun($content);
$sheet->getComment($Cell)->setWidth(‘100pt’);
$sheet->getComment($Cell)->setHeight(‘100pt’);
$sheet->getComment($Cell)->setMarginLeft(‘150pt’);
$sheet->getComment($Cell)->getFillColor()->setRGB(‘EEEEEE’);
}

/*
* 现在单元格的有效数据范围,暂时仅限于数字
* $sheet 当前的工作簿对象
* $Cell 需要设置属性的单元格
* $ValueRange array 允许输入数组的访问
*/
private static function setValueRange($sheet,$Cell,$ValueRange)
{
//设置单元格的的数据类型是数字,并且保留有效位数
$sheet->getStyle($Cell)->getNumberFormat()->setFormatCode(PHPExcel_Style_NumberFormat::FORMAT_NUMBER_00);

$ValueRange = explode(",",$ValueRange);

//开始数值有效访问设定
$objValidation = $sheet->getCell($Cell)->getDataValidation();
$objValidation->setType( PHPExcel_Cell_DataValidation:: TYPE_WHOLE );
$objValidation->setErrorStyle( PHPExcel_Cell_DataValidation:: STYLE_STOP );
$objValidation->setAllowBlank(true);
$objValidation->setShowInputMessage( true); //设置显示提示信息
$objValidation->setShowErrorMessage( true); //设置显示错误信息
$objValidation->setErrorTitle(‘输入错误’); //错误标题
$objValidation->setError(‘请输入数据范围在从’.$ValueRange[0].’到’.$ValueRange[1].’之间的所有值’); //错误内容
$objValidation->setPromptTitle(‘允许输入’); //设置提示标题
$objValidation->setPrompt(‘请输入数据范围在从’.$ValueRange[0].’到’.$ValueRange[1].’之间的所有值’); //提示内容
$objValidation->setFormula1($ValueRange[‘0’]); //设置最大值
$objValidation->setFormula2($ValueRange[‘1’]); //设置最小值

}

private static function OutinputHeader($objWriter)
{
$fileName = str_replace(‘.php’, ‘.xlsx’, pathinfo(__FILE__, PATHINFO_BASENAME));
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header(‘Content-Disposition:inline;filename="’.$fileName.’"’);
header("Content-Transfer-Encoding: binary");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Pragma: no-cache");
$objWriter->save(‘php://output’);
exit;
}

//数据控制,设置单元格数据在一个可选方位类
private static function setSelectionRange($sheet,$Cell,$rangeStr,$Title="数据类型")
{

$objValidation = $sheet->getCell($Cell)->getDataValidation();
$objValidation -> setType(PHPExcel_Cell_DataValidation::TYPE_LIST)
-> setErrorStyle(PHPExcel_Cell_DataValidation::STYLE_STOP)
-> setAllowBlank(true)
-> setShowInputMessage(true)
-> setShowErrorMessage(true)
-> setShowDropDown(true)
-> setErrorTitle(‘输入的值有误’)
-> setError(‘您输入的值不在下拉框列表内.’)
-> setPromptTitle(‘"’.$Title.’"’)
-> setFormula1(‘"’.$rangeStr.’"’);
}

/*
* 构建表头
* */
public static function RecursionCreateExecl($head,$data)
{

$PHPExecl = new PHPExcel();

$objWriter = self::createWriter($PHPExecl, ‘Excel2007’);

$PHPExecl->getProperties()->setCreator("4399om")
->setLastModifiedBy("Summer")
->setTitle("Office 2007 XLSX Test Document")
->setSubject("Office 2007 XLSX Test Document")
->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.")
->setKeywords("office 2007 openxml php")
->setCategory("Test result file");

$PHPExecl->setActiveSheetIndex(0);

$sheet = $PHPExecl->getActiveSheet();

self::HandleHeadToNode($sheet, $head,1,0,0);

self::SummerInsertDateToExecl($sheet,$head,$data,4);

self::OutinputHeader($objWriter);
}

private static function HandleHeadToNode($sheet,$Head,$beginRow,$col,$StartCol)
{

foreach($Head as $key=>$cells)
{
$row = $beginRow; //表示行

$beginCol = PHPExcel_Cell::stringFromColumnIndex($col).$row;

$sheet->getCell($beginCol)->setValue($cells[‘value’]);

//设置表格样式
$sheet->getStyle($beginCol)->getAlignment()->applyFromArray(
array(
‘horizontal’=> PHPExcel_Style_Alignment::HORIZONTAL_CENTER,
‘vertical’ => PHPExcel_Style_Alignment::VERTICAL_CENTER,
‘rotation’ => 0,
‘wrap’ => TRUE,
)
);

$sheet->getStyle($beginCol)->getFont()->getColor()->setARGB(PHPExcel_Style_Color::COLOR_DARKGREEN);

//设置单元格的宽度
if(isset($cells[‘width’]))
{
$Cell = $sheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($col));
$Cell->setWidth($cells[‘width’]);
}

//哥元素打上标记
if(isset($cells[‘Content’]))
{
self::setComment($sheet, $beginCol, $cells[‘Content’]);
}

$merge = false; //合并单元格
if(isset($cells[‘col’]))
{
$col += $cells[‘col’]-1;
$merge = true;
}

if(isset($cells[‘row’]))
{
$row += $cells[‘row’]-1;
$merge = true;
}

if($merge)
{
$endCol = PHPExcel_Cell::stringFromColumnIndex($col).$row;
$sheet->mergeCells($beginCol.":".$endCol);
}

$row ++;
$col ++;

//表示有存在孩子节点
if(isset($cells[‘children’]) && is_array($cells[‘children’])){
$cols = $StartCol;
if(!self::IsExistChildren($cells[‘children’]))
{
$cols = $col-2;
$StartCol = $col;
}

self::HandleHeadToNode($sheet,$cells[‘children’],$row,$cols,$StartCol);
}else{
$StartCol = $col;
}

}

}

//判断自己的孩子节点中是否存在孙子节点
private static function IsExistChildren($Data)
{
foreach($Data as $key=>$value)
{
if(isset($value[‘children’]) && is_array($value[‘children’]))
{
return true;
}
}
return false;
}

//获取底层数据
private static function getHead($Head,&$Node=array())
{
foreach($Head as $key=>$value)
{
if(isset($value[‘children’]) && is_array($value[‘children’]))
{
self::getHead($value[‘children’],$Node);
}
else
{
$Node[] = $value;
}
}

return $Node;
}

}

$Head = array(
array(‘value’=>’姓名’,’col’=>2,’row’=>2,’width’=>20,’type’=>’list’,’allowarray’=>’PHP开发工程师,PHP开发’),
array(‘value’=>’第一天’,’col’=>2,’row’=>1,’width’=>20,’Content’=>’2014-12-29号’,
‘children’=>
array(
array(‘value’=>’上午’,’col’=>1,’width’=>20,’type’=>’range’,’allowarray’=>’10,100′),
array(‘value’=>’下午’,’width’=>20),
),

),
array(‘value’=>’第二天’,’col’=>2,’row’=>1,’width’=>20,
‘children’=>
array(
array(‘value’=>’上午’,’width’=>20),
array(‘value’=>’下午’,’width’=>20),
),
),
);

$data = array(
array(‘PHP开发工程师’,’12’,’吃饭1′,’睡觉1′,’起床刷牙2′,’吃饭睡觉2′),
array(‘PHP开发工程师’,’25’,’吃饭1′,’睡觉1′,’起床刷牙2′,’吃饭睡觉2′),
array(‘PHP开发工程师’,’50’,’吃饭1′,’睡觉1′,’起床刷牙2′,’吃饭睡觉2′),
array(‘PHP开发工程师’,’99’,’吃饭1′,’睡觉1′,’起床刷牙2′,’吃饭睡觉2′),
array(‘PHP开发工程师’,’10’,’吃饭1′,’睡觉1′,’起床刷牙2′,’吃饭睡觉2′),
);

$Node = PHPExeclCore::RecursionCreateExecl($Head,$data);

[/php]

得到的效果也基本符合需求

PHP导入Execl表

PHP导入Execl表

 

 

下载项目源码

远程登录正方教务系统(绕过验证码篇)

我都不想说什么,我研究了半年的验证码识别,最后发现,原来正方教务系统登录方式可以绕过验证码,,简直吐血

开始打算做一个采集教务系统成绩和课表的功能,但验证码成了拦路虎。

我“机智”用获取验证码,然后cookies记录验证码,再跟正方服务器对比来完成验证码登录,,最后发现这步可以直接跳过去。

正方有一个功能:后台开启验证码,就是他的验证码功能是可选的。说不白了,就是你不请求服务器就不会生成验证码。

然后,你只需要几行代码就是完成远程登录教务系统,主要隐藏域

[php]

public function login(){
$url = C(‘School_url’)."default2.aspx";
$post[‘__VIEWSTATE’] = ‘dDwyODE2NTM0OTg7Oz69gdYBizSXHwj9xCoAQeszC5eGfw==’;
$post[‘__VIEWSTATEGENERATOR’] = ‘92719903’;
$post[‘txtUserName’] = ‘学号’;
$post[‘TextBox2’] = ‘密码’;
$post[‘txtSecretCode’] = ”;
$post[‘lbLanguage’] = ”;
$post[‘RadioButtonList1’] = iconv(‘utf-8’, ‘gb2312’, ‘学生’);
$post[‘Button1’] = iconv(‘utf-8’, ‘gb2312’, ‘登录’);
$result = $this->curl_Login($url,$post);
print_r($result);
return $result;
}

[/php]

然后是CURL请求的代码,,记得cookies这个东西是必须要有的,PHP开启的curl跟游览器不是一个进程,不共用cookies

所以要用CURL的抓取cookies的方法

[php]

curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file); //在CURL请求中发送cookies值

curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file); //在CURL请求中,把head头的cooked存入到指定的文件中

[/php]

使用这两个方法就能操作curl中的cookies。

然后,,你就会发现,你没有输入验证码通过学号和密码就已经登录到教务系统了。然后怎么回去成绩和课表呢?

 

一道面试题,两段代码引发的思考

在一本面试书上面看到一个面试题

题目大意:

给你一个二维数组,数组是有序的,从左往右依次递增的顺序排序,从上往下下的依次递增的顺序排序。这样给你一个值,你判断该元素是否存在数值中。存在返回true,不存在返回false

不经过大脑可以给出一个解法方法:

[php]

function searchArry($array,$search)
{
$result = array();
foreach($array as $key=>$value)
{
if($key == $search)
{
return true;
}
foreach($value as $k=>$v)
{
if($v == $search)
{
return true
}
}
}
return true;
}
[/php]

第一种实现发现的思路很简单: 遍历 ,全部遍历一般就OK了

然后,我又在想,上面方法1跟PHP完全没啥关系。接着就来一个方法2

[php]

function searchArry2($array, $search)
{
if(key_exists($search, $array))
{
return true;
}
else
{
foreach($array as $key)
{
if(in_array($search,$key))
{
return true;
}
}
}
return false;
}
[/php]

第二种解法其实是跟第一种一样的思想是:PHP是用二维数值来表示矩阵的,那么,第一横排或者第一竖列就可以表示数值一维的键值,然后遍历,这里只是套用了PHP的函数接口

两种实现,在思路上面完全没有差异,但结果差异很大。。当我把二维数值开到1000*10000的时候,,结果就差距很明显了

PHP算法执行时间比较

PHP算法执行时间比较

对大数值,两种实现方式差在内存消耗是一样,这是因为都是遍历全数值的原因,,但在时间上面差的比较大,从数据上面看是几个数量级。

这两种实现的方式还没有扣中题目的重点,但确说明了另一个问题:在用PHP代码的时候,尽量要使用PHP内置的函数代替。

其实PHP在判断数组键值存在时候,isset($array[$key]) 比 key_exists() 的效率要高的多,,这就是哈希数值的优越性。

上面的思考和比较都跟原来的题目没有太大的关联。