王骏的博客 http://blog.okbase.net/JO2000 ThinkPHP的ISAPI_Rewrite静态转写规则 http://blog.okbase.net/JO2000/archive/56433.html JO2000 2017/10/18 21:43:38 Apache环境下ThinkPHP的转写规则是这样的:

<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>

但在IIS里用ISAPI_Rewrite3来转写,上面的规则不好使,可以采用如下规则:

<IfModule mod_rewrite.c>
  RewriteEngine On
  Options +FollowSymLinks
  
  RewriteRule ^(Public|admin)($|/) - [NC,L]
  
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-f
  
  RewriteRule ^/$ /home/index/index [NC,L]
  RewriteRule ^index.php/(.*)$ index.php/$1 [NC,L]
  RewriteRule ^(.*)$ index.php/$1 [NC,L]
</IfModule>

把首页转写为 /home/index/index,并过滤掉Public目录的转写。

]]>
PHP应该下载安装线程安全还是非线程安全版本? http://blog.okbase.net/JO2000/archive/56430.html JO2000 2017/10/17 11:38:08 Windows下的PHP分为线程安全Thread Safe和非线程安全Non Thread Safe版本。

如果是使用ISAPI的方式来运行PHP就必须用Thread Safe(线程安全)的版本;
而用FastCGI模式运行PHP的话就没有必要用线程安全检查了,用None Thread Safe(NTS,非线程安全)的版本运行效率更高。

 

在PHP的下载页面http://windows.php.net/download,对于PHP5有下面几个版本:
1)x86 Non Thread Safe
这是32位非线程安全版本, 适合以FastCGI模式运行PHP
2)x86 Thread Safe
这是32位线程安全版本,适合以ISAPI方式运行PHP
3)x64 Non Thread Safe
这是64位非线程安全版本, 适合64位操作系统中以FastCGI模式运行PHP
4)x64 Thread Safe
这是64位线程安全版本,适合64位操作系统中以ISAPI方式运行PHP

 

]]>
微擎通过订阅号借权(认证服务号)获取微信用户信息 http://blog.okbase.net/JO2000/archive/56428.html JO2000 2017/10/13 20:34:10  

粉丝未关注公众号获取资料时,需要弹出确认授权的登录框,粉丝同意后公众号才可以获取到用户信息,此操作需要手动调动函数来完成。 通过 mc_oauth_userinfo() 来获取粉丝信息。

 

$fans = $_W['fans'];
if(empty($fans['nickname'])){
    load()->model('mc');
    $fans = mc_oauth_userinfo();
}
var_dump($fans);

更多信息请参考:https://www.kancloud.cn/donknap/we7/135455

]]>
微擎 Hack (1) – 启动过程 http://blog.okbase.net/JO2000/archive/56423.html JO2000 2017/10/11 20:30:34 作者:huangwc

由于在开发过程中,发现有一些很不厚道的设置,让我不得不试图去修改一些微擎的 Kernel 代码,下面将探索的过程笔记一下。


同时也记录一下我的问题本身: 我希望重用微擎的用户系统,微擎的设计中,用户相关的库表在 ims_members 相关的表中,主键是 uid; 可以通过用户登录微擎后台,通过统一的验证使用后台的逻辑; 一个公众号只能捆绑到一个用户上面(用户指定当前公众号的时候,该公众号会指定给该用户),通过右上角的公众号切换处切换,一个用户切换上去,原来的用户就会被切换下来(这一点太蛋疼了,以至于无法做出稳定的用户 – 管理的微信号假设,问题还在后面)。 在没有设置当前公众号的情况下,很多功能会返回错误页面——请您从“管理公众号”或是从顶部“切换公众号”选择要操作的公众号! 我现在要做的事情是:要创建一些其他的系统角色用户,但是必须能够自己设置权限访问到我插件指定的 site.php 和 mobile.php 指定的模块,这些页面在这种情况下,都会显示第4点的错误信息。 那么现在我要做的就是,在 kernel 里面把这个跳过去。


一、哪些公众号是当前登录用户可以管理的(设置为当前)

全局搜索代码——“请切换公众号”,定位到 frame.tpl.php

发现点击下方的公众号按钮会跳转这个 url:

create_url(‘account/switch’, array(‘id’ => $account[‘weid’]));

就是这段,继续深究:

发现获取当前“可切换公众号”的逻辑在 account.mod.php 的 account_search() 方法中:

如果是根用户 $_W[‘isfounder’] = true 或者 ims_wechats 的 uid(公众号管理员)是当前登录的 uid 的时候,可以切换到该 ims_wechats.weid 指定的公众号。

 

二、“切换公众号”的动作在哪里

以上,找到 /source/controller/account/switch.ctrl.php

业务逻辑如下: 先从 $_GPC 即 get post cookie 获取 id 字段; 用这个 id 获取 ims_wechats 里面的公众号记录; 检查公众号是否存在,并且当前用户是否有权限管理这个公众号; 执行切换操作; 执行切换操作是如下几行:

cache_write(‘weid:’ . $_W[‘uid’], $row[‘weid’]); isetcookie(‘__weid’, $row[‘weid’], 7 * 46800); message($row[‘name’], ”, ‘success’);

干得好!就在这里,结论应该就是在这里: 1. 只要 $_GPC 定义了 weid,并且当前用户有权限,“当前公众户就是这个”; 2. 加载的同时还会将 __weid 写入 cookie,作为当前客户访问的 __weid; 3. 同时还会将 {key: ‘weid:{weid}’, value: uid} 写入 ims_cache 表;


三、ims_cache 里面的这个 weid:{weid} 干什么用的

看起来在全局的 cache 里面将公众号指定给一个用户,就已经将这个公众号绑死给一个人了,这样极是危险,起码 yy 一下就觉得只要在同一个公众号上面重复 switch 还是会把另一个人踢出去。

于是找一下 ‘weid:’ 这个关键词,看看 cache 里面这个东西是造嘛的。

只有在 cache.mysql.func.php 的 cache_read($key) 方法里面有,再查找引用。

经过几层查找,发现 cache.func.php 里面 有个 cache_load($key, $unserialize) 函数,会将 ims_cache 的键值对加载到 $_W 中。

… 好乱。。暂不深究算了。


四、结论及测试

只要在【一】中有权限的登录用户,在 cookie 里面 __weid 字段指定了用户是当前用户,就可以将当前公众号设置为当前公众号。

实测:在 chrome 中直接在 js console 里面运行 cookie.set(‘__weid’, 1),公众号就切换到了,另外在 firefox 里面设置另一个用户,互不影响。

于是,如果想让一个用户登录可以绑定公众号,只需要写一下 cookie 然后刷新页面即可。

或者,在 querystring 里面加入 &__weid=1,也可以突破这个限制!

但是,现在问题是,如果一个用户不具有公众号的管理权限,这一点就有难度了。

但是我实测了一下,居然是可以的!!

这下就一点问题都没有了!

 

转载自:http://www.huangwenchao.com.cn

]]>
微擎模块的安装文件manifest.xml http://blog.okbase.net/JO2000/archive/56382.html JO2000 2017/9/23 22:46:44 微擎在安装或卸载模块时会根据manifest.xml生成(或删除)数据库中相应记录,并执行manifest.xml里指定的脚本。

manifest.xml文件内容详细介绍如下:

 

manifest - xmlns *(新增)*
用来为此模块XML的命令空间,此处必须填写"http://www.we7.cc".

manifest - versionCode
用来说明当前模块适用于哪个版本的微擎, 用来保证模块的兼容性. 多个支持的版本请使用逗号隔开.

manifest - application 用来定义模块的基本设置属性
manifest - application - setting 用来说明此模块是否有针对模块的设置项, 设置项可以保存此模块需要的配置参数(此参数针对不同的公众号分别保存)

manifest - application - name
模块的名称

manifest - application - identifie
模块标识符, 应对应模块文件夹的名称, 微擎系统按照此标识符查找模块定义

manifest - application - version
模块当前版本, 此版本用于模块的版本更新

manifest - application - type *(新增)*
模块的类型,方便在左侧菜单中归类与显示, 目前分为 business(主要业务),customer(客户关系),activity(营销及活动),services(常用服务及工具),other(其他)

manifest - application - ability
模块功能描述, 使用简单的语言描述模块的作用, 来吸引用户

manifest - application - description
模块详细描述, 详细介绍模块的功能和使用方法

manifest - application - author
模块的作者, 留下你的大名吧

manifest - application - url
模块的发布页, 可以通过这个url来访问你的模块最新情况

manifest - platform
用来定义模块用以处理公众平台消息的设置项

manifest - platform - subscribes
消息订阅器定义(消息订阅器提供了一种处理公众平台消息的方式, 可以接受到指定类型的消息, 来进行分析和统计, 不能用以处理消息返回结果. 这种处理是并行的, 同一个消息会被每一个订阅它的模块接收到)

manifest - platform - subscribes - message
定义需要被订阅器订阅的消息类型, 这里的消息被 WeModuleReceiver 处理

manifest - platform - handles
消息处理器定义(消息处理器用于接收公众平台的消息, 并返回相应的处理结果. 这种处理是互斥的, 同一个消息只能从一个模块返回处理结果)

manifest - platform - handles - message
定义需要被处理器处理的消息类型, 这里的消息被 WeModuleProcessor 处理

manifest - platform - rule *(变更)*
定义此模块是否需要规则触发

manifest - platform - rule - embed
当前模块进行消息处理时需要定义规则, 是否使用规则路由. (使用规则路由必须要能处理text类型消息, handles节点中必须包含 )

manifest - bindings *(新增)*
定义此模块的封面,管理菜单,微站菜单及规则扩展菜单

manifest - bindings - cover
定义模块的封面入口,封面入口为单条图文信息即是模块需要对用户开放的入口地址.

manifest - bindings - cover - call
定义模块动态扩展菜单项, 此值对应 WeModuleSite 类中的方法, 返回的值结构与entry相同, 将成为此节点的菜单项.

manifest - bindings - cover - entry
模块绑定菜单的定义结构. 需要定义 title - 操作的名称, do - 模块操作入口, state - 附加的用户参数(定义于WeModuleSite)

manifest - bindings - rule
定义规则的附加操作, 每个entry代表一个附加操作.

manifest - bindings - menu
定义模块在左侧本模块菜单下拉列表中的附加菜单操作, 每一个entry代表一个菜单操作.

manifest - bindings - home
定义模块在微站首页的扩展菜单项, 每一个entry代表一个微站首页菜单项.

manifest - bindings - profile
定义模块在微站个人中心的扩展菜单项, 每一个entry代表一个微站个人中心菜单项.

manifest - bindings - shortcut
定义模块在微站快捷菜单的扩展菜单项, 每一个entry代表一个微站快捷菜单项.

manifest - install
安装执行脚本, 这里支持两种形式: php脚本和sql语句. 如果安装时只需要写入数据库相关内容, 可以在此直接定义sql语句. 也可以使用php文件, 例如: install.php 代表执行模块定义目录下的 install.php

manifest - uninstall
卸载执行脚本, 参上

manifest - upgrade
升级执行脚本, 参上

]]>
思途旅游CMS系统简单分析 http://blog.okbase.net/JO2000/archive/56377.html JO2000 2017/9/9 21:41:10 思途旅游CMS系统采用Kohana框架,Kohana是一款纯PHP5的框架,基于MVC模式开发,它的特点是高安全性,轻量级代码,容易使用。

购买的是正版的思途旅游CMS系统,因需要进行二次开发,所以对这个系统进行简单的分析。

 

控制器 Controller

基类:Stourweb_Controller

v5\classes\stourweb\controller.php

其它控制器位于:v5\classes\controller 目录下,
例如首页index控制器 index.php


视图 View

视图基类 Stourweb_View
位于 \v5\classes\stourweb\view.php

 

其它它视图位于:v5\views\default

例如默认的首页模板位于 v5\views\default\index

 

模型 Model

位于 tools\classes\model
基类ORM,实际上是Kohana_ORM,文件位于core\modules\orm\classes\kohana\orm.php

]]>
Excel统计重复内容的数量 http://blog.okbase.net/JO2000/archive/56367.html JO2000 2017/8/24 9:52:58 需要用到公式COUNTIF

格式是:COUNTIF(查找范围,条件)

 

1、返回包含值12的单元格数量
=COUNTIF(DATA,12)
2、返回包含负值的单元格数量
=COUNTIF(DATA,"<0")
3、返回不等于0的单元格数量
=COUNTIF(DATA,"<>0")
4、返回大于5的单元格数量
=COUNTIF(DATA,">5")
5、返回等于单元格A1中内容的单元格数量
=COUNTIF(DATA,A1)
6、返回大于单元格A1中内容的单元格数量
=COUNTIF(DATA,">''&A1)
7、返回包含文本内容的单元格数量
=COUNTIF(DATA,''*'')
8、返回包含三个字符内容的单元格数量
=COUNTIF(DATA,''???'')
9、返回包含单词"GOOD"(不分大小写)内容的单元格数量
=COUNTIF(DATA,''GOOD'')
10、返回在文本中任何位置包含单词"GOOD"字符内容的单元格数量
=COUNTIF(DATA,"*GOOD*")
11(1)、返回包含以单词"AB"(不分大小写)开头内容的单元格数量
=COUNTIF(DATA,"AB*")
11(2)、返回包含以单词"AB"(不分大小写)结尾内容的单元格数量
=COUNTIF(DATA,"*AB")
12、返回包含当前日期的单元格数量
=COUNTIF(DATA,TODAY())
13、返回大于平均值的单元格数量
=COUNTIF(DATA,">"&AVERAGE(DATA))
14、返回平均值上面超过三个标准误差的值的单元格数量
=COUNTIF(DATA,">"&AVERAGE(DATA)+STDEV(DATA)*3)
15、返回包含值为3或-3的单元格数量
=COUNTIF(DATA,3)+COUNIF(DATA,-3)
16、返回包含值;逻辑值为TRUE的单元格数量
=COUNTIF(DATA,TRUE)
17、统计区域中不为空的单元格个数(数值、文本、空格都算)——(上述第3条:文本也算不等于0,空格不算)
=Countif(DATA,"<>")
18、只统计文本单元格数量,不统计数值和空格——(上述第7条统计含空格)
=COUNTIF(DATA,"><")

 

注意:因为只能处理长度小于15的数字,所以对于更长的数字,我们需要用加上"*"这样的方式:

COUNTIF($D$2:$D$5000, D2&"*")

]]>
PHP如何获取文件的扩展名 http://blog.okbase.net/JO2000/archive/56364.html JO2000 2017/8/21 22:55:39 比较常用的方法是使用pathinfo,代码如下:

 

$ext = pathinfo($filename, PATHINFO_EXTENSION);

 

如果需要处理非ASCII字符,需要在此之前:setlocale(LC_ALL,'en_US.UTF-8');

 

另外,对于想获取URL网址的扩展名,例如这个网址:http://example.com/file/okbase.mp3?a=1&b=2
需要用到parse_url,例如:


pathinfo(parse_url($url)['path'], PATHINFO_EXTENSION); // 返回mp3

dirname(parse_url($url)['path']) // 返回 /file
basename(parse_url($url)) // 返回 okbase.mp3

]]>
weex, react native, ionic 技术选型 http://blog.okbase.net/JO2000/archive/56316.html JO2000 2017/8/4 23:22:20

[编者按:为什么在App开发界,没有像曾经的VB,Delphi那样简单易用,又能跨Android,iOS,一统江湖的开发框架呢?现在的框架总是有这样那样的不爽!]

 

目前有一个 APP 项目,需要同时开发 iOS 版和 Android 版,并且每个平台又分为客户端、管理员端,也就是说相当于四个应用。而我们人员有限,工期有明确限制,全部使用 native 语言开发,基本不现实。所以决定采用类 react native 的方案开发。

到了技术选型这里非常纠结!做了一下简单的对比

React Native

如果从成熟度来看,react native 是不二选择。但是,需要维护两套代码,即 iOS,Android 各一套。并且开发调试 iOS 只能使用 Mac 设备。

优势

  • 文档全
  • 社区成熟
  • 组件丰富

劣势

  • 需要维护两套代码
  • 团队没有 React 经验
  • 对开发设备有要求,调试不方便

Weex

而 weex 可以做到 iOS, android, H5 共用一套代码,但是,这个是国内阿里维护的项目,靠谱度存疑,遇到问题估计很难查到资料。而且我们需要使用蓝牙访问硬件设备,而 weex 并没有现成的组件实现,需要我们自己用 native 来实现。

优势

  • 三端共用一套代码
  • 调试简单
  • 团队都有 Vuejs 实战经验

劣势

  • 国内开源项目不太靠谱
  • 社区极度不成熟
  • 文档不全
  • 组件有限,且下载量都在两三百的量,质量无法保证
  • 需要 native 代码扩展蓝牙数据读取

ionic

ionic 这个虽然比较成熟,但是唯一的问题是大家普遍反映体验不够流畅。

优势

  • 文档全
  • 社区成熟
  • 组件丰富

劣势

  • angularjs 不太靠谱
  • 流畅度存疑

结论

决定先硬着头皮上 Weex 了,蓝牙支持准备参考 react native 的实现。

 

来源:http://www.sunzhongwei.com/weex-react-native-ionic-technology-selection

]]>
SFP光模块和SFP+光模块有何区别 http://blog.okbase.net/JO2000/archive/56313.html JO2000 2017/8/3 10:34:16 通俗地讲,最主要的区别是:SFP是千兆(G)SFP+是万兆(10G)

外观基本上差不多,万兆光模块稍显粗壮,拿在手里万兆光模块会感觉重一些。

请看具体的定义:

 

一、SFP的定义

SFP(Small form-factor pluggable)意思是小型可拔插式。就是能够支持千兆以太网、SONET、光纤通道和其他通信标准,插入到交换机SFP端口的可拔插模块。SFP规范是基于IEEE802.3和SFF-8472,它们能够支持速度高达4.25 Gbps。由于其较小的尺寸,SFP取代了以前常见的千兆接口转换器(GBIC),因此也被称为迷你GBIC SFP。通过选择不同的波长和端口的SFP模块,交换机上的相同的电端口可以连接上不同接头和不同波长的光纤。


二、SFP+的定义


由于SFP仅支持4.25 Gbps的传输速率,满足不了人们对网速越来越高的要求,SFP+就在这样的背景下诞生了。SFP+的最高传输速率可达16 Gbps,事实上,SFP+是一个增强版本的SFP。SFP+规范是基于SFF-8431。在今天的大多数应用,SFP+模块通常支持8 Gbit/s光纤通道。SFP+模块凭借其体积小、使用方便的优点,取代了早期万兆以太网中使用比较多的XENPAK和XFP模块,成为了万兆以太网中最受欢迎的光模块。

对上面的SFP和SFP+定义进行分析之后可以得出,SFP和SFP+之间的主要区别是传输速率。而且由于数据速率的不同,应用和传输距离也不同。下面就是它们在应用方面的一些不同。

]]>