分类 PHP 下的文章

哈希表hashTable:哈希表hashTable是一种通过哈希函数,将特定的键映射到特定值的一种数据结构,它维护键和值之间一一对应关系。

    对于高级语言内核来讲,哈希表hashTable绝对是数据结构中的关键,很多高级语言是显示的支持哈希表hashTable的,哈希表hashTable的实践应用也非常广泛,编译器会维护一个符号表来保存标记,提供增删改查等操作。

    设计合理的哈希表hashTable的时间复杂度是O(1),而最最糟糕的哈希表hashTable时间复杂度为O(n),通常,它总是O(1)。而O(n)是因为,哈希表hashTable变成了一个链表。

    名词解释:

    1、键:操作数据的标识,例如PHP数组中的索引。

    2、槽:用于保存数据的坑。

    3、哈希映射函数:将键映射到数据应该存放的槽所在位置的函数。

    4、哈希冲突:哈希函数将两个不同的键映射到同一个索引的情况。

    哈希表hashTable可以理解为关联数组,数组使用键来寻址,如果键的范围较小且是数字的话, 我们可以直接使用数组来完成哈希表hashTable,而如果关键字范围太大,如果直接使用数组我们需要为所有可能的键申请空间。 很多情况下这是不现实的。即使空间足够,空间利用率也会很低,这并不理想。同时键也可能并不是数字, 在PHP中尤为如此,所以人们使用一种映射函数(哈希函数)来将键映射到特定的域中。

    通过合理设计的哈希函数,我们就能将键映射到合适的范围,因为我们的键空间可以很大(例如字符串键), 在映射到一个较小的空间中时可能会出现两个不同的键映射被到同一个index上的情况, 这就是我们所说的出现了冲突。 目前解决哈希表hashTable冲突的方法主要有两种:链接法和开放寻址法。目前PHP中哈希冲突解决方法就是链接法。

    冲突解决 - 连接法:

    链接法通过使用一个链表来保存槽值的方式来解决冲突,也就是当不同的键映射到一个槽中的时候使用链表来保存这些值。 所以使用链接法是在最坏的情况下,也就是所有的键都映射到同一个槽中了,这样哈希表hashTable就退化成了一个链表, 这样的话操作链表的时间复杂度则成了O(n),这样哈希表hashTable的性能优势就没有了, 所以选择一个合适的哈希函数是最为关键的。由于目前大部分的编程语言的哈希表hashTable实现都是开源的,大部分语言的哈希算法都是公开的算法, 虽然目前的哈希算法都能良好的将键进行比较均匀的分布,而这个假使的前提是键是随机的,正是由于算法的确定性, 这就导致了别有用心的黑客能利用已知算法的可确定性来构造一些特殊的键,让这些键都映射到 同一个槽位导致哈希表hashTable退化成单链表,导致程序的性能急剧下降,从而造成一些应用的吞吐能力急剧下降, 尤其是对于高并发的应用影响很大,通过大量类似的请求可以让服务器遭受DoS(服务拒绝攻击), 这个问题一直就存在着,只是最近才被各个语言重视起来。哈希冲突攻击利用的哈希表hashTable最根本的弱点是:开源算法和哈希实现的确定性以及可预测性, 这样攻击者才可以利用特殊构造的键来进行攻击。要解决这个问题的方法则是让攻击者无法轻易构造 能够进行攻击的键序列。PHP采用的是一种治标不治本的做法: 限制用户提交数据字段数量。这样可以避免大部分的攻击,不过应用程序通常会有很多的数据输入方式,比如,SOAP,REST等等, 比如很多应用都会接受用户传入的JSON字符串,在执行json_decode()的时候也可能会遭受攻击。 所以最根本的解决方法是让哈希表hashTable的碰撞键序列无法轻易的构造,目前PHP中还没有引入不增加额外的复杂性情况下的完美解决方案。

    冲突解决 - 开放寻址法:

    通常还有另外一种解决冲突的方法:开放寻址法。使用开放寻址法是槽本身直接存放数据, 在插入数据时如果键所映射到的索引已经有数据了,这说明发生了冲突,这是会寻找下一个槽, 如果该槽也被占用了则继续寻找下一个槽,直到寻找到没有被占用的槽,在查找时也使用同样的策略来进行。由于开放寻址法处理冲突的时候占用的是其他槽位的空间,这可能会导致后续的键在插入的时候更加容易出现 哈希冲突,所以采用开放寻址法的哈希表hashTable的装载因子不能太高,否则容易出现性能下降。



相关内容:用PHP的实现一个高效的数据库(文件存储,NOSQL)  使用哈希表来构建自己的数据库 

    CoreSeek安装比较麻烦,官方手册对此的支持并不算很好。CoreSeek是基于Sphinx的中文的分词和全文检索软件。本文是在MAC OS X系统下安装和调试CoreSeek。

    安装过程中报错如果是警告warning则忽略,如果是错误error,则必须要处理。

    CoreSeek是支持三种数据来源的,一种是众所周知的Mysql,一种是XML文件,另一种是Python。而Python则是万能数据类型。在本CoreSeek安装测试教程中只示例数据源是XML文件和MYSQL。


    官方手册地址:http://www.coreseek.cn/products-install/install_on_macosx


    一、设置环境变量


$ export PATH=/usr/local/bin:$PATH

$ export LC_ALL=zh_CN.UTF-8

$ export.UTF-8


    二、安装依赖库:m4、autoconf、automake、libtool。

注意:不要brew install 来安装,因为对安装的库的版本有要求。

$ curl -O -L http://mirrors.kernel.org/gnu/m4/m4-1.4.13.tar.gz

$ tar -xzvf m4-1.4.13.tar.gz

$ cd m4-1.4.13

$ sudo ./configure --prefix=/usr/local/opt

$ sudo make

$ sudo make install

$ cd ..


$ curl -O -L http://mirrors.kernel.org/gnu/autoconf/autoconf-2.65.tar.gz

$ tar -xzvf autoconf-2.65.tar.gz

$ cd autoconf-2.65

$ sudo ./configure --prefix=/usr/local/opt

$ sudo make

$ sudo make install

$ cd ..


$ curl -O -L http://mirrors.kernel.org/gnu/automake/automake-1.11.tar.gz

$ tar xzvf automake-1.11.tar.gz

$ cd automake-1.11

$ ./configure --prefix=/usr/local/opt

$ sudo make

$ sudo make install

$ cd ..


$ curl -O -L http://mirrors.kernel.org/gnu/libtool/libtool-2.2.6b.tar.gz

$ tar xzvf libtool-2.2.6b.tar.gz

$ cd libtool-2.2.6b

$ sudo ./configure --prefix=/usr/local/opt

$ sudo make

$ sudo make install

$ cd ..


    三、安装Mysql。

1、mysql 的安装自行安装

2、查找mysql头文件地址和库文件地址。我用

brew install mysql

安装的Mysql,头文件地址和库文件地址分别是/usr/local/Cellar/mysql/5.6.17_1/include/mysql 和 /usr/local/Cellar/mysql/5.6.17_1/lib。

头文件地址就是mysql.h所在的目录,库文件地址就是libmysqlclient.a所在的目录。


    四、下载Coreseek。

$ curl -O -L http://www.coreseek.cn/uploads/csft/3.2/coreseek-3.2.14.tar.gz

$ tar xzvf coreseek-3.2.14.tar.gz

$ cd coreseek-3.2.14

在coreseek-3.2.14文件夹下有mmseg和csft和testpack。mmseg是分词服务,csft是CoreSeek的核心服务,testpack是测试用例。


    五、安装mmseg

$ cd mmseg-3.2.14

$ sudo ./bootstrap

$ sudo ./configure --prefix=/usr/local/opt/mmseg3

$ sudo make

$ sudo make install

$ cd ..

在make的时候,可能会报错,如下

file included from css/ThesaurusDict.cpp:6:

../src/css/ThesaurusDict.h:12:17: error: expected namespace name

using namespace __gnu_cxx;

^

css/ThesaurusDict.cpp:79:15: warning: result of comparison against a string

literal is unspecified (use strncmp instead) [-Wstring-compare]

if (filename == "-") {

    ^ ~~~

    css/ThesaurusDict.cpp:116:15: warning: result of comparison against a string

literal is unspecified (use strncmp instead) [-Wstring-compare]

if (filename != "-") {

    ^ ~~~

    2 warnings and 1 error generated.

    make[2] : *** [ThesaurusDict.lo] Error 1

make[1]: *** [install-recursive] Error 1


这个时候make进程已经终止。原因是因为编译器版本太高导致的,修改方法:1是降低编译器版本,反正我打死也不愿意。方法2如下:

vim src/css/ThesaurusDict.h

###在头部找到:#include <string>

###再其下加入一行代码:

#include <ext/hash_map>

修改完后保存退出,继续重新sudo make一下,就没有error级错误了,然后sudo make install即可。


    六、安装coreseek

$ cd csft-3.2.14

$ sudo sh buildconf.sh

$ sudo ./configure --prefix=/usr/local/opt/coreseek  --without-unixodbc --with-mmseg --with-mmseg-includes=/usr/local/opt/mmseg3/include/mmseg/ --with-mmseg-libs=/usr/local/opt/mmseg3/lib/ --with-mysql --with-mysql-includes=/usr/local/Cellar/mysql/5.6.17_1/include/mysql --with-mysql-libs=/usr/local/Cellar/mysql/5.6.17_1/lib

$ sudo make

$ sudo make install

$ cd ..

在./configure时,参数--with-mysql-includes是mysql头文件位置,--with-mysql-libs是mysql库文件位置,请在本CoreSeek安装教程第三步所记录的mysql头文件地址和库文件地址,替换。


在make时,可能又会出现error级的错误,如果出现make程序是停止运行的,必须要修改。错误提示如下:

phinxexpr.cpp:1047:11: error: use of undeclared identifier 'ExprEval'

T val = ExprEval ( this->m_pArg, tMatch ); // 'this' fixes gcc ...


解决方法:修改源代码。

vim src/sphinxexpr.cpp

将T val = ExprEval( this->m_pArg, tMatch )替换为T val = this->ExprEval ( this->m_pArg, tMatch )。

就是加了个“this->”,是把这个文件中所有的ExprEval()函数都修改了,有三四个吧。

修改后保存退出,重新sudo make,然后sudo make install即可。


    七、测试XML数据

$ cd testpack


#测试编码是否正确显示中文,如果不是中文则请看本CoreSeek安装测试教程第一步

$ cat var/test/test.xml


# 测试mmseg分词的效果

$ /usr/local/opt/mmseg3/bin/mmseg -d /usr/local/opt/mmseg3/etc var/test/test.xml 


# 建立检索的索引。

$ /usr/local/opt/coreseek/bin/indexer -c etc/csft.conf --all


#全文搜索“网络搜索”

$ /usr/local/opt/coreseek/bin/search -c etc/csft.conf 网络搜索

如果在建立检索的索引出错,FATAL: failed to lock var/data/xml.spl: Resource temporarily unavailable, will not index. Try --rotate option.

则修改为

$ /usr/local/opt/coreseek/bin/indexer -c etc/csft.conf --all --rotate



    八、测试MYSQL数据源

cd testpack


1、修改配置文件,文件位于testpack/etc/csft_mysql.conf

vim etc/csft_mysql.conf

我的csft_mysql.conf文件如下:记得把mysql 的sql_host,sql_user,sql_pass,sql_db,sql_port修改为自己的,并且把路劲都修改为你自己的路径。

#MySQL数据源配置,详情请查看:http://www.coreseek.cn/products-install/mysql/

#请先将var/test/documents.sql导入数据库,并配置好以下的MySQL用户密码数据库


#源定义

source mysql

{

    type                    = mysql


    sql_host                = localhost

    sql_user                = root

    sql_pass                = 8823150

    sql_db                    = test

    sql_port                = 3306

    sql_query_pre            = SET NAMES utf8


    sql_query                = SELECT id, group_id, UNIX_TIMESTAMP(date_added) AS date_added, title, content FROM documents

                                                              #sql_query第一列id需为整数

                                                              #title、content作为字符串/文本字段,被全文索引

    sql_attr_uint            = group_id           #从SQL读取到的值必须为整数

    sql_attr_timestamp        = date_added #从SQL读取到的值必须为整数,作为时间属性


    sql_query_info_pre      = SET NAMES utf8                                        #命令行查询时,设置正确的字符集

    sql_query_info            = SELECT * FROM documents WHERE id=$id #命令行查询时,从数据库读取原始数据信息

}


#index定义

index mysql

{

    source            = mysql             #对应的source名称

    path            = /Users/lane/coreseek-3.2.14/testpack/var/data/mysql #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/...

    docinfo            = extern

    mlock            = 0

    morphology        = none

    min_word_len        = 1

    html_strip                = 0


    #中文分词配置,详情请查看:http://www.coreseek.cn/products-install/coreseek_mmseg/

    charset_dictpath = /usr/local/opt/mmseg3/etc/ #BSD、Linux环境下设置,/符号结尾

    #charset_dictpath = etc/                             #Windows环境下设置,/符号结尾,最好给出绝对路径,例如:C:/usr/local/coreseek/etc/...

    charset_type        = zh_cn.utf-8

}


#全局index定义

indexer

{

    mem_limit            = 128M

}


#searchd服务定义

searchd

{

    listen                  =   9312

    read_timeout        = 5

    max_children        = 30

    max_matches            = 1000

    seamless_rotate        = 0

    preopen_indexes        = 0

    unlink_old            = 1

    pid_file = /Users/lane/coreseek-3.2.14/testpack/var/log/searchd_mysql.pid  #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/...

    log = /Users/lane/coreseek-3.2.14/testpack/var/log/searchd_mysql.log        #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/...

    query_log = /Users/lane/coreseek-3.2.14/testpack/var/log/query_mysql.log #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/...

}


2、给mysql导入测试数据

    mysql的测试数据我们用的是test数据库,documents数据表。请自行确保test数据库存在,我们一起来建documents表,这个表的结构和数据都是由CoreSeek提供的,在testpack/var/test/documents.sql

mysql -u root -p

#输入密码


#看看,数据库test在不在

mysql > show databases; 

#如果test库不在请创建

mysql > create database test;


use test;


#导入数据

source /Users/lane/coreseek-3.2.14/testpack/var/test/documents.sql


3、测试:

$ cd testpack


# 建立检索的索引

$ /usr/local/opt/coreseek/bin/indexer -c etc/csft_mysql.conf --all


#全文搜索“网络搜索”

$ /usr/local/opt/coreseek/bin/search -c etc/csft_mysql.conf 网络搜索

如果提示有错误,请检查csft_mysql.conf的路径、mysql的配置等信息是否正确。



    九、测试PHP+MYSQL

1、先启动服务

/usr/local/opt/coreseek/bin/searchd -c etc/csft.conf

2、PHP文件

<?php

require ( "/Users/lane/coreseek-3.2.14/testpack/api/sphinxapi.php" );


$cl = new SphinxClient ();

$cl->SetServer ( '127.0.0.1', 9312);

$cl->SetConnectTimeout ( 3 );

$cl->SetArrayResult ( true );

$cl->SetMatchMode ( SPH_MATCH_ANY);

$res = $cl->Query ( '网络搜索', "*" );

print_r($cl);

print_r($res);



我在Linux折腾了一天没有搞定,在MAC搞了半天搞定了。等搞定Linux后再发Linux的。

PHP微信快速开发框架LaneWeChat新版本V1.2发布于2014-08-17,新增自定义菜单的管理,包括添加新菜单,清空所有菜单,获取菜单列表等。


新增:

一、自定义菜单

    1、自定义菜单的管理。

    2、包括添加新菜单。

    3、清空所有菜单。

    4、获取菜单列表。

二、多媒体上传与下载(获取Media_id的途径)

    1、多媒体上传

    2、多媒体下载


详细文档:http://www.lanecn.com/article/main/aid-65



框架内容已经正式迁移到了http://lanewechat.lanecn.com请移步,点击PHP微信开发框架传送




框架名称:LaneWeChat   微信开发框架PHP


框架版本:1.2


框架简介:这是一个为快速开发微信应用而生的PHP框架。将微信的开发者功能根据文档进行了封装。为了快速开发的目的,开发者完全不需要要知道具体是如何实现的,只需要简单的调用方法即可。微信框架LaneWeChat,经过中国联通、奇虎360等多家公司使用的微信PHP框架,轻量、高效。完美的封装了PHP微信开发的功能点,只需要调用封装好的函数,5分钟上手,10分钟精通PHP微信开发。LaneWeChat微信开发框架为全国大中小企业提供了接入微信的好机会。


开发语言:PHP


版本要求:原则PHP5.3以上


版本规避:若版本低于PHP5.3,则删除本框架所有页面开头namespace一行即可。


命名空间:本框架的命名空间均为LaneWeChat开头。


下载地址:https://github.com/lixuancn/LaneWeChat/archive/master.zip


GitHub:https://github.com/lixuancn/LaneWeChat


目前还没有微信支付这些,只有微信公众账号的相关功能,希望大家闲暇时间也可以贡献代码。



Developer Blog:http://www.lanecn.com


文档地址:http://www.lanecn.com/article/main/aid-65





更新日志:


    2014-08-17:1.2版本。新增自定义菜单功能,多媒体上传与下载(media_id的获取途径)。更新说明:http://www.lanecn.com/article/main/aid-66


    2014-08-07:1.0版本





文档目录:


    1、常识普及。


    2、如何安装。


    3、初出茅庐。


    4、流程分析。


    5、牛刀小试。


    6、函数详解。


    7、实例示范。



常识普及:


一、微信公众账号分两种,一种是订阅号,一种是服务号。


    1、订阅号是被动响应用户消息功能,并且每天推送一条消息。


    2、服务号是300元/每年认证,被动响应用户消息,主动给用户发送消息,自定义菜单按钮,网页授权等功能,并且每月推送一条消息。


    3、订阅号适合消息类,新闻类应用,常常需要推送文章给用户的;服务号适合自助查询等。


    4、订阅号被认证后也享用自定义菜单等功能,仍旧是300元/每年




二、专业术语:


    1、OpenId:微信服务器并不会告诉公众号用户的微信ID,即使是你的关注者也不行,为了解决开发中唯一标识的问题,微信使用了OpenId,所谓的OpenId,就是用户和微信公众号之间的一种唯一关系。一个用户在一个公众号面前,享用唯一的OpenId,不会和别人重复。换言之,同一个用户在另一个公众号面前,是拥有另一个OpenId的。再直白些就是$openId = md5('用户微信ID+公众号ID')


    2、Access_Token:此项只有认证号的功能才会使用的到,Access_token是一个授权标识,即一个授权验证码,一个标识10分钟内有效,10分钟的有效期内公众号的多个关注者可以使用同一个Access_Token。在使用主动给指定用户发送消息、自定义菜单、用户管理和用户组管理等功能的时候,每次操作需要给微信服务器以参数的形式附带Access_token。


    3、Access_Token网页版:本Access_Token网页版授权时会使用到,和2中的Access_Toekn是不同的东西,不过使用我们的LaneWeChat微信快速开发框架是不需要了解这些的。Access_Token网页版是说在用户打开你的公众号提供的网页的时候,你的网页需要获取用户的OpenId、昵称、头像等信息的时候授权用的。同时,本Access_Token网页版有两种用法,一种是打开网页后弹出一个授权框,让用户点击是否授权,界面像主流的开放平台授权界面(比如QQ登陆某网站,支付宝账号登陆某网站等);另一种是不需要弹出授权框仍旧可以获取用户信息,用法可以在实例中看到。



如何安装:


    1、本框架以代码包的插件形式放在项目的目录中即可。


    2、配置项:打开根目录下的config.php,修改定义常量WECHAT_APPID,WECHAT_APPSECRET,WECHAT_URL。其中前两项可以在微信公众号官网的开发者页面中找到,而WECHAT_URL是你微信项目的URL,以http://开头


    3、本框架的唯一入口为根目录下的wechat.php


    4、首次使用时,请打开根目录下的wechat.php,注释掉20行,21行,并且打开注释第24行。


    5、在微信开发者-填写服务器配置页面,填写URL为http://www.lanecn.com/wechat.php,保证该URL可以通过80端口正常访问(微信服务器目前只支持80端口),并且将Token填写为config.php中的WECHAT_TOKEN常量的内容(可以修改)。


    6、微信服务器在第4步验证通过后,反向操作第4步,即注释掉第24行,打开注释第20行,21行。至此,安装配置完成。





初出茅庐:


    1、给你的微信公众号发送一条文本消息,比如hello world或者其他什么的。这个时候你应该会收到一条“收到文本”的服务器反馈的被动响应的消息。


    2、这个时候你需要先为自己鼓掌。





流程分析:


    1、我们给微信服务器发送了一条“hello world”的文本消息。


    2、微信服务器收到我们的消息后,查找该公众账号所配置的服务器信息中的URL(如何安装部分 - 第5步)。


    3、微信服务器向第二步获取的URL发送请求,参数是微信服务器自己拼接过的XML格式。


    4、根目录下的wechat.php,引入了我们的配置文件和所需的类后,进入了类WeChat的方法run()。该类位于core/wechat.lib.php。微信的XML数据此时已经被解析为数组,变量名为$request。


    5、然后,我们进入了类WechatRequest的方法switchType(),根据不同的消息类型,给予不同的响应。比如用户发送文本消息和关注事件,给出的返回应该是不同的。当然,你要给出同样的提示也不能说是错的。


    6、在第5步中的方法中,是一个switch,根据消息类型(此时是文本类型,微信服务器给我的是text)选择了一个处理文本消息的方法,类WechatRequest中的方法text()。该方法的功能是发送文本消息,文本内容是“收到文本”。


    7、此时,我们return了一个数据返回给了上层调用,层层return,就到了我们根目录的下的唯一入口文件wechat.php,此时我们返回的数据被echo出来了。


    8、微信服务器拿到了输出的数据,微信服务器进行分析和处理,将文本发送给了用户的微信客户端。我们就在手机上看到了微信输出的“收到文本”。


    9、流程结束,这就是发送“hello world”,然后返回给用户“收到文本”。



牛刀小试:


    1、打开core/wechatrequest.php文件,讲方法text()中的变量修改为$content = '收到文本消息'.$request['content'];


    2、保存并且上传到你的服务器。


    3、在微信中打开你的公众号,输入文本消息“hello world”。见证奇迹的时刻到了。这个时候你的手机微信客户端中现实的是“收到文本消息hello world”。





函数详解:


    一、被动给用户发送消息。


        1、类简介:用户输入文本、图片、语音、音乐、视频等消息,以及关注、取消关注,上报地理位置等事件后,服务器被动给出应答。


        2、使用命名空间:use LaneWeChat\Core\ResponsePassive;


        3、参数:  $fromusername = "谁发给你的?(用户的openId)"  在变量$request['fromusername']中


                 $tousername = "你的公众号Id";                 在变量$require['tousername']中


                 $mediaId = "通过上传多媒体文件,得到的id。";


        4、发送文本


                ResponsePassive::text($fromusername, $tousername, '文本消息内容');


        5、发送图片


                ResponsePassive::image($fromusername, $tousername, $mediaId);


        6、发送语音


                ResponsePassive::voice($fromusername, $tousername, $mediaId);


        7、发送视频


                ResponsePassive::video($fromusername, $tousername, $mediaId, '视频标题', '视频描述');


        8、发送音乐


                ResponsePassive::music($fromusername, $tousername, '音乐标题', '音乐描述', '音乐链接', '高质量音乐链接,WIFI环境优先使用该链接播放音乐', '缩略图的媒体id,通过上传多媒体文件,得到的id');


        9、发送图文


            1)创建图文消息内容


                $tuwenList = array();


                $tuwenList[] = array('title'=>'标题1', 'description'=>'描述1', 'pic_url'=>'图片URL1', 'url'=>'点击跳转URL1');


                $tuwenList[] = array('title'=>'标题2', 'description'=>'描述2', 'pic_url'=>'图片URL2', 'url'=>'点击跳转URL2');


            2)构建图文消息格式


                $itemList = array();


                foreach($tuwenList as $tuwen){


                    $itemList[] = ResponsePassive::newsItem($tuwen['title'], $tuwen['description'], $tuwen['pic_url'], $tuwen['url']);


                }


            3)发送图文消息


                ResponsePassive::news($fromusername, $tousername, $itemList);





二、AccessToken授权。


    1、类简介:除了被动相应用户之外,在主动给用户发送消息,用户组管理等高级操作,是需要AccessToken授权的,我们调用一个URL给微信服务器,微信服务器会返回给我们一个散列字符串,在高级操作的时候需要将此串以参数的形式发送。散列字符串10分钟内有效,过期需要重新获取,获取新的后之前的全部失效。


    2、使用命名空间:use LaneWeChat\Core\AccessToken;


    3、参数:无


    4、获取AccessToken


        AccessToken::getAccessToken(); 该调用会返回微信服务器散列后的AccessToken字符串。


    5、温馨提示


        如果暂且用不到此功能,请跳过。最后来看这里!


    6、功能补充


        有一个地方需要用户自行完善,根据介绍我们已经知道了,获取AccessToken只有10分钟的有效期,过期需要重新获取。因此,我们需要存储这个AccessToken。


        由于大家的存储方式各不相同,有Mysql的,有Redis的,有MongoDB的,还有Session的。所以这里我讲存储和读取给留空了。


        流程:AccessToken类,public方法只有一个,就是getAccessToken()。这个方法会调用一个私有方法_checkAccessToken()来检测AccessToken是否存在并且是否过期,如果不存在或过期,则调用私有方法_getAccessToken()


        完善步骤:


        1)、打开core/accesstoken.lib.php文件。


        2)、私有方法_getAccessToken()的倒数第二行(return是倒数第一行),在这个地方,请讲变量$accessTokenJson存储起来,变量$accessTokenJson是一个字符串。


        3)、私有方法_checkAccessToken()的第一行就是读取操作(有一行伪代码$accessToken = YourDatabase::get('access_token');),将刚才第二步的存储的东西给读出来,并且赋值给$accessToken。


        4)、在第二步的存储,第三部的读取的时候,请不要修改数据,仅仅完善一个读和存的操作就可以了。





三、主动给用户发送消息。


        1、类简介:用户输入文本、图片、语音、音乐、视频等消息,以及关注、取消关注,上报地理位置等事件后,服务器被动给出应答。


        2、使用命名空间:use LaneWeChat\Core\ResponsePassive;


        3、参数  $tousername = "你的公众号Id";                 在变量$require['tousername']中


                $mediaId = "通过上传多媒体文件,得到的id。";


        4、发送文本内容


        ResponseInitiative::text($tousername, '文本消息内容');


        5、发送图片


        ResponseInitiative::image($tousername, $mediaId);


        6、发送语音


        ResponseInitiative::voice($tousername, $mediaId);


        7、发送视频


        ResponseInitiative::video($tousername, $mediaId, '视频描述', '视频标题');


        8、发送地理位置


        ResponseInitiative::music($tousername, '音乐标题', '音乐描述', '音乐链接', '高质量音乐链接,WIFI环境优先使用该链接播放音乐', '缩略图的媒体id,通过上传多媒体文件,得到的id');


        9、发送图文消息


            1)创建图文消息内容


                $tuwenList = array();


                $tuwenList[] = array('title'=>'标题1', 'description'=>'描述1', 'pic_url'=>'图片URL1', 'url'=>'点击跳转URL1');


                $tuwenList[] = array('title'=>'标题2', 'description'=>'描述2', 'pic_url'=>'图片URL2', 'url'=>'点击跳转URL2');


            2)构建图文消息格式


                $itemList = array();


                foreach($tuwenList as $tuwen){


                    $itemList[] = ResponseInitiative::newsItem($tuwen['title'], $tuwen['description'], $tuwen['pic_url'], $tuwen['url']);


                }


            3)发送图文消息


                ResponseInitiative::news($tousername, $itemList);





四、用户及用户组管理。


        1、类简介:获取粉丝列表,创建\修改用户组,讲用户添加\移除到用户组。


        2、使用命名空间:use LaneWeChat\Core\UserManage;


        3、参数  $openId = '用户和微信公众号的唯一ID';           在变量$require['openid']中


                $mediaId = "通过上传多媒体文件,得到的id。";


                $groupId = '分组ID';                         在添加新分组、获取分组列表的时候可以得到


        4、分组管理 - 创建分组


            UserManage::createGroup('分组名');


        5、分组管理 - //获取分组列表


            UserManage::getGroupList();


        6、分组管理 - 查询用户所在分组


            UserManage::getGroupByOpenId($openId);


        7、分组管理 - 修改分组名


            UserManage::editGroupName($groupId, '新的组名');


        8、分组管理 - 移动用户分组


            UserManage::editUserGroup($openId, $groupId);


        9、用户管理 - 获取用户基本信息


            UserManage::getUserInfo($openId);


        10、用户管理 - 获取关注者列表


            UserManage::getFansList($next_openId='');


        11、用户管理 - 获取网络状态


            UserManage::getNetworkState();





五、网页授权。


        1、类简介:在网页中获取来访用户的数据。


        2、使用命名空间:use LaneWeChat\Core\WeChatOAuth;


        3、参数  $openId = '用户和微信公众号的唯一ID';           在变量$require['openid']中


                $mediaId = "通过上传多媒体文件,得到的id。";


                $groupId = '分组ID';                         在添加新分组、获取分组列表的时候可以得到


        4、获取CODE。


            参数:$scope:snsapi_base不弹出授权页面,只能获得OpenId;snsapi_userinfo弹出授权页面,可以获得所有信息


            参数:$redirect_uri:将会跳转到redirect_uri/?code=CODE&state=STATE 通过GET方式获取code和state。获取CODE时,发送请求和参数给微信服务器,微信服务器会处理后将跳转到本参数指定的URL页面


            WeChatOAuth::getCode($redirect_uri, $state=1, $scope='snsapi_base');


        5、通过code换取网页授权access_token(access_token网页版)。首先请注意,这里通过code换取的网页授权access_token,与基础支持中的access_token不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。


            参数:$code getCode()获取的code参数。$code = $_GET['code'];


            WeChatOAuth::getAccessTokenAndOpenId($code);





六、多媒体上传下载


        1、类简介:在网页中获取来访用户的数据。上传的多媒体文件有格式和大小限制,如下:


            * 图片(image): 1M,支持JPG格式


            * 语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式


            * 视频(video):10MB,支持MP4格式


            * 缩略图(thumb):64KB,支持JPG格式


            * 媒体文件在后台保存时间为3天,即3天后media_id失效


        2、使用命名空间:use LaneWeChat\Core\Media;


        3、参数  $filename 上传的文件的绝对路径


                $type 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)


                $mediaId = "通过上传多媒体文件,得到的id。";


                $groupId = '分组ID';                         在添加新分组、获取分组列表的时候可以得到


        4、上传:上传后,微信服务器会返回一个mediaId。


            Media::upload($filename, $type);


        5、下载:根据mediaId下载一个多媒体文件。


            Media::download($mediaId);





七、自定义菜单


        1、类简介:添加自定义菜单。最多可以有三个一级菜单,每个一级菜单最多可以有五个菜单。一级菜单最多4个汉字,二级菜单最多7个汉字。创建自定义菜单后,由于微信客户端缓存,需要24小时微信客户端才会展现出来。建议测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。


            摘自微信官方网站:目前自定义菜单接口可实现两种类型按钮,如下:


            click:


                用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event的结构给开发者,并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;


            view:


                用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的url值(即网页链接),达到打开网页的目的,建议与网页授权获取用户基本信息接口结合,获得用户的登入个人信息。


            总结一下哦,就是微信的菜单分两种,一种是view型,就是你设置一个网址,点了这个菜单之后就跳到你设置的网址去了。另一种就是click型,你设置一个key,然后用户点击的时候会通过本框架唯一入口wechat.php发送一个消息类型为event的请求,在wechatrequest.lib.php文件下的eventClick方法中可以使用。


        2、使用命名空间:use LaneWeChat\Core\Menu;


        3、设置菜单:是所有的菜单数据全部发送一次,可不是每新增一个只发一个菜单。


            Menu::setMenu($menuList);


            $menuLis 是菜单列表,结构如下:


            $menuList = array(


                                array('id'=>'1', 'pid'=>'0', 'name'=>'顶级分类一', 'type'=>'', 'code'=>''),


                                array('id'=>'2', 'pid'=>'1', 'name'=>'分类一子分类一', 'type'=>'2', 'code'=>'lane_wechat_menu_1_1'),


                                array('id'=>'3', 'pid'=>'1', 'name'=>'分类一子分类二', 'type'=>'1', 'code'=>'http://www.lanecn.com'),


                                array('id'=>'4', 'pid'=>'0', 'name'=>'顶级分类二', 'type'=>'1', 'code'=>'http://www.php.net/'),


                                array('id'=>'5', 'pid'=>'0', 'name'=>'顶级分类三', 'type'=>'2', 'code'=>'lane_wechat_menu_3'),


                            );


            'id'是您的系统中对分类的唯一编号;


            'pid'是该分类的上级分类,顶级分类则填写0;


            'name'是分类名称;


            'type'是菜单类型,数字1或者2,1是view类型,2是click类型,如果该分类下有子分类请务必留空;


            'code'是view类型的URL或者click类型的自定义key,如果该分类下有子分类请务必留空。


        4、获取微信菜单:获取到的是已经设置过的菜单列表,格式为Json,是微信服务器返回的原始数据。


            Menu::getMenu();


        5、删除微信菜单:将会删除设置过的所有菜单(一键清空)。


            Menu::delMenu();





实例示范:


    1、通过网页授权获得用户信息


        场景:用户点击了我的自定义菜单,或者我发送的文本消息中包含一个URL,用户打开了我的微信公众号的网页版,我需要获取用户的信息。


        代码:


        <?php

            use LaneWeChat\Core\WeChatOAuth;

            use LaneWeChat\Core\UserManage;


            //第一步,获取CODE

            WeChatOAuth::getCode('http://www.lanecn.com/index.php', 1, 'snsapi_base');

            //此时页面跳转到了http://www.lanecn.com/index.php,code和state在GET参数中。

            $code = $_GET['code'];

            //第二步,获取access_token网页版

            $openId = WeChatOAuth::getAccessTokenAndOpenId($code);

            //第三步,获取用户信息

            $userInfo = UserManage::getUserInfo($openId['openid']);

        ?>


    2、更多实例正在补充。

    二叉树遍历,是值从根节点出发,按照某种次序依次访问二叉树中的所有节点,使得每个节点被访问一次且仅被访问依次。

tupan062.gif

图是百度搜的。。。谢谢提供图的英雄。。

    前序遍历二叉树:如果二叉树为空则返回,若二叉树非空,则先遍历左树,再遍历右树,遍历顺序为ABCDEGF。

    中序遍历二叉树:如果二叉树为空则返回,若二叉树非空,则从根节点开始,中序遍历根节点的左子树,然后是访问根节点,最后中序遍历右子树,遍历顺序为CBEGDFA。

    后序遍历二叉树:如果二叉树为空则返回,若二叉树非空,则从左到右先叶子后节点的访问遍历访问左右子树,最后是访问根节点。访问顺序为CGEFDBA。

    层序遍历二叉树:如果二叉树为空则返回,若二叉树非空,则从树的第一层,也就是根节点开始访问,从上而下逐层遍历,在同一层中,按照从左到右的顺序对节点逐个访问。访问顺序为ABCDEFG。


    现在,我们用PHP代码,来遍历二叉树结构。二叉树是放一个大数组,每一个节点都有三个字段,data表示这个节点的值,lChild表示这个节点的左边子节点,rChild表示这个节点的右边子节点。二叉树的结构我们用上面那张图。


二叉树结构代码如下:

<?php

//二叉树

$arr = array(

    'data' => 'A',

    'lChild' => array(

        'data' => 'B',

        'lChild' => array(

            'data' => 'C',

            'lChild' => array(),

            'rChild' => array(),

        ),

        'rChild' => array(

            'data' => 'D',

            'lChild' => array(

                'data' => 'E',

                'lChild' => array(),

                'rChild' => array(

                    'data' => 'G',

                    'lChild' => array(),

                    'rChild' => array(),

                ),

            ),

            'rChild' => array(

                'data' => 'F',

                'lChild' => array(),

                'rChild' => array(),

            ),

        ),

    ),

    'rChild' => array(),

);


遍历算法一:前序遍历二叉树

<?php

//前序遍历二叉树算法

echo '前序遍历二叉树算法:';

PreOrderTraverse($arr);

echo '<Br>';

function PreOrderTraverse($node){

    if(empty($node)){

        return;

    }

    //输出值

    print_r($node['data']);

    //左节点

    PreOrderTraverse($node['lChild']);

    //右节点

    PreOrderTraverse($node['rChild']);

}


遍历算法二:中序遍历二叉树

<?php

//中序遍历二叉树算法

echo '中序遍历二叉树算法:';

inOrderTraverse($arr);

echo '<Br>';

function inOrderTraverse($node){

    if(empty($node)){

        return;

    }

    //左节点

    inOrderTraverse($node['lChild']);

    //输出值

    print_r($node['data']);

    //右节点

    inOrderTraverse($node['rChild']);

}


遍历算法三:后序遍历二叉树

<?php

//后序遍历二叉树算法

echo '后序遍历二叉树算法:';

postOrderTraverse($arr);

echo '<Br>';

function postOrderTraverse($node){

    if(empty($node)){

        return;

    }

    //左节点

    postOrderTraverse($node['lChild']);

    //右节点

    postOrderTraverse($node['rChild']);

    //输出值

    print_r($node['data']);

}