php中实现精确设置session过期时间的方法 保持登录状态

http://www.jb51.net/article/52309.htm

大多数据情况下我们对于session过期时间使用的是默认设置的时间,而对于一些有特殊要求的情况下我们可以设置一下session过期时间。

对此,可以在PHP中,设置php.ini,找到session.gc_maxlifetime = 1440 #(PHP5默认24分钟)
这里你可以随便设置一下过期时间.但是有人说设置以后,好象不起作用!
其实不是不起作用,而是因为系统默认:

1
2
session.gc_probability = 1
session.gc_divisor = 1000

garbage collection 有个概率的,1/1000就是session 1000次才有一次被回收。
只要你的访问量大了,那就能达到回收的效果.
或者你也可以设置一下session.gc_divisor 的值,
比如:session.gc_divisor = 1,这样就能明显的看到SESSION过期的效果了.

我们最常用的是在php程序中设置,如下例程序所示:

1
2
3
4
<?php
if(!isset($_SESSION['last_access'])||(time()-$_SESSION['last_access'])>60)
$_SESSION['last_access'] = time();
?>

这样就搞定了,如果要设置已过期的话也可以在程序中实现:

1
2
3
<?php
unset($_SESSION['last_access']);// 或 $_SESSION['last_access']='';
?>

session有过期的机制:

session.gc_maxlifetime 原来session 过期是一个小概率的事件,分别使用session.gc_probability和session.gc_divisor 来确定运行session 中gc 的概率 session.gc_probability和session.gc_divisor的默认值分别为 1和100。分别为分子和分母 所以session中gc的概率运行机会为1% 。如果修改这两个值,则会降低php的效率。所以这种方法是不对的!!
因此,修改php.ini文件中的gc_maxlifetime变量就可以延长session的过期时间了:(例如,我们把过期时间修改为86400秒)
session.gc_maxlifetime = 86400
然后,重启你的web服务(一般是apache)就可以了。

session“回收”何时发生:

默认情况下,每一次php请求,就会有1/100的概率发生回收,所以可能简单的理解为“每100次php请求就有一次回收发生”。这个概率是通过以下参数控制的
#概率是gc_probability/gc_divisor

1
2
session.gc_probability = 1
session.gc_divisor = 100

注意1:假设这种情况gc_maxlifetime=120,如果某个session文件最后修改时间是120秒之前,那么在下一次回收(1/100的概率)发生前,这个session仍然是有效的。

注意2:如果你的session使用session.save_path中使用别的地方保存session,session回收机制有可能不会自动处理过期session文件。这时需要定时手动(或者crontab)的删除过期的session:

1
cd /path/to/sessions; find -cmin +24 | xargs rm

PHP中的session永不过期

不修改程序是最好的方法了,因为如果修改程序,测试部一定非常郁闷,那么只能修改系统环境配置,其实很 简单,打开php.ini设置文件,修改三行如下:

1、session.use_cookies

把这个的值设置为1,利用cookie来传递sessionid

2、session.cookie_lifetime

这个代表SessionID在客户端Cookie储存的时间,默认是0,代表浏览器一关闭SessionID就作废……就是因为这个所以PHP的 session不能永久使用! 那么我们把它设置为一个我们认为很大的数字吧,999999999怎么样,可以的!就这样。

3、session.gc_maxlifetime

这个是Session数据在服务器端储存的时间,如果超过这个时间,那么Session数据就自动删除! 那么我们也把它设置为99999999。

就这样一切ok了,当然你不相信的话就测试一下看看——设置一个session值过个10天半个月的回来看看,如果你的电脑没有断电或者宕机,你仍然可以看见这个sessionid。

当然也可能你没有控制服务器的权限并不能像我一样幸运的可以修改php.ini设置,一切依靠我们自己也是有办法的,当然就必须利用到客户端存储 cookie了,把得到的sessionID存储到客户端的cookie里面,设置这个cookie的值,然后把这个值传递给session_id()这 个函数,具体做法如下:

1
2
3
4
5
6
7
8
9
<?php
session_start(); // 启动Session 
$_SESSION['count']; // 注册Session变量Count 
isset($PHPSESSID)?session_id($PHPSESSID):$PHPSESSID = session_id(); 
// 如果设置了$PHPSESSID,就将SessionID赋值为$PHPSESSID,否则生成SessionID 
$_SESSION['count']++; // 变量count加1 
setcookie('PHPSESSID', $PHPSESSID, time()+3156000); // 储存SessionID到Cookie中 
echo $count; // 显示Session变量count的值 
?>

session失效不传递

我们先写个php文件:<?=phpinfo()?>, 传到服务器去看看服务器的参数配置。
转到session部分,看到session.use_trans_sid参数被设为了零。
这个参数指定了是否启用透明SID支持,即session是否随着URL传递。我个人的理解是,一旦这个参数被设为0,那么每个URL都会启一个session。这样后面页面就无法追踪得到前面一个页面的session,也就是我们所说的无法传递。两个页面在服务器端生成了两个session文件,且无关联。(此处精确原理有待确认)
所以一个办法是在配置文件php.ini里把session.use_trans_sid的值改成1。

当然我们知道,不是谁都有权限去改php的配置的,那么还有什么间接的解决办法呢?
下面就用两个实例来说明:
文件1 test1.php

1
2
3
4
5
6
7
8
9
10
<?php
//表明是使用用户ID为标识的session
session_id(SID);
//启动session
session_start();
//将session的name赋值为Havi
$_SESSION['name']="Havi";
//输出session,并设置超链接到第二页test2.php
echo "<a href="test2.php" rel="external nofollow" >".$_SESSION['name']."</a>";
?>

文件2: test2.php

1
2
3
4
5
6
7
8
<?php
表明是使用用户ID为标识的session
session_id(SID);
//启动session
session_start();
//输出test1.php中传递的session。
echo "This is ".$_SESSION['name'];
?>

所以,重点是在session_start();前加上session_id(SID);,这样页面转换时,服务器使用的是用户保存在服务器session文件夹里的session,解决了传递的问题。
不过有朋友会反映说,这样一来,多个用户的session写在一个SID里了,那Session的价值就发挥不出来了。所以还有一招来解决此问题,不用加session_id(SID);前提是你对服务器的php.ini有配置的权限:
output_buffering改成ON,道理就不表了。
第二个可能的原因是对服务器保存session的文件夹没有读取的权限,还是回到phpinfo.php中,查看session保存的地址:

1
session.save_path: var/tmp

所以就是检查下var/tmp文件夹是否可写。
写一个文件:test3.php来测试一下:

1
2
3
<?php
echo var_dump(is_writeable(ini_get("session.save_path")));
?>

如果返回bool(false),证明文件夹写权限被限制了,那就换个文件夹咯,在你编写的网页里加入:

1
2
3
4
5
6
7
//设置当前目录下session子文件夹为session保存路径。
$sessSavePath = dirname(__FILE__).'/session/';
//如果新路径可读可写(可通过FTP上变更文件夹属性为777实现),则让该路径生效。
if(is_writeable($sessSavePath) && is_readable($sessSavePath))
{
session_save_path($sessSavePath);
}

register_shutdown_function 函数详解

php开发测试及线上的时候可以根据不同情况设定错误和异常处理需要用到有

设定错误和异常处理三函数

  1. register_shutdown_function(array(‘Debug’,‘fatalError’)); //定义PHP程序执行完成后执行的函数
  2. set_error_handler(array(‘Debug’,’appError’)); // 设置一个用户定义的错误处理函数
  3. set_exception_handler(array(‘Debug’,‘appException’)); //自定义异常处理。

下面讲一下register_shutdown_function() 函数.

功能:register_shutdown_function() 函数可实现当程序执行完成后执行的函数,其功能为可实现程序执行完成的后续操作。程序在运行的时候可能存在执行超时,或强制关闭等情况,但这种情况下默认的提示是非常不友好的,如果使用register_shutdown_function()函数捕获异常,就能提供更加友好的错误展示方式,同时可以实现一些功能的后续操作,如执行完成后的临时数据清理,包括临时文件等。

可以这样理解调用条件:

1、当页面被用户强制停止时

2、当程序代码运行超时时

3、当PHP代码执行完成时,代码执行存在异常和错误、警告

实例说明

1. 一个简介的例子 , 调用一个函数

  1. <?php
  2. function test() {
  3.     echo “test()”;
  4. }
  5. register_shutdown_function(array(“test”));
  6. echo “show: “;
  7. ?>

这个例子输出

  1.  show: test()    

这个函数的作用就是在退出脚本前,调用已经注册的函数,并执行该函数。

2. 在类中用一个数组作为一个参数

  1. <?php
  2.     class ClassDemo {
  3.         public function __construct() {
  4.             register_shutdown_function(array($this, “f”));
  5.         }
  6.  
  7.         public function f() {
  8.             echo “f()”;
  9.         }
  10.     }
  11.  
  12.     $demo = new ClassDemo();
  13.     echo “before </br>”;
  14. ?>

输出:

  1. before
  2. f()

3. 带有参数的例子:

  1. <?php
  2. function f($str) {
  3.         echo $str.“<br>”;
  4. }
  5.  
  6. register_shutdown_function(“f”,“hello”);
  7.     class ClassDemo {
  8.         public function __construct() {
  9.             register_shutdown_function(array($this, “f”),“hello”);
  10.         }
  11.  
  12.         public function f($str) {
  13.             echo “f():”.$str;
  14.         }
  15.     }
  16.  
  17.    $demo = new ClassDemo();
  18.    echo “before </br>”;  
  19. ?>

注意事项

1,register_shutdown_function()函数可重复调用,但执行的顺序与注册的顺序相同
2,如果在调用register_shutdown_function()函数之前有exit()函数调用,register_shutdown_function()函数将不能执行
3,PHP4后支持注册函数参数传递
4,在某些服务端,如Apache,当前目录在register_shutdown_function()函数中能够改变
5,register_shutdown_function()函数执行在headers发送之后
总而言之 出现500错误(不友好错误)。把这段代码放在文件前面。执行,即可看到错误信息。

register_shutdown_function(function(){ var_dump(error_get_last()); });

PHP微服务框架即Micro Service Framework For PHP

Micro Service Framework For PHP

PHP微服务框架即“Micro Service Framework For PHP”,是Camera360社区服务器端团队基于Swoole自主研发现代化的PHP协程服务框架,简称msf或者php-msf,是Swoole的工程级企业应用框架,经受了Camera360亿级用户高并发大流量的考验。php-msf由Camera360服务器团队主导研发,会持续更新与维护,也希望有更多优秀的Swoole应用实践开发者加入。php-msf核心设计思想是采用协程、异步、并行的创新技术手段提高系统的单机吞吐能力,降低整体服务器成本。

主要特性

  • 精简版的MVC框架
  • IO密集性业务的单机处理能力提升5-10倍
  • 代码常驻内存
  • 支持对象池
  • 支持Redis连接池、MySQL连接池
  • 支持Redis分布式、master-slave部署结构的集群
  • 支持异步、并行
  • 基于PHP Yield实现协程
  • 内建http/redis/mysql/mongodb/task等协程客户端
  • 纯异步的Http Server
  • RPC Server/Client
  • 支持命令行模式
  • 支持独立进程的定时器
  • 支持独立配置进程

环境要求

  • Linux,FreeBSD,MacOS(有兼容问题)
  • Linux内核版本2.3.32以上(支持epoll)
  • PHP-7.0及以上版本(生产环境建议使用PHP-7.1)
  • gcc-4.4以上版本
  • swoole-1.9.15及以上版本(暂不支持Swoole-2.0)
  • hiredis-0.13.3
  • yac
  • phpredis
  • composer

文档

框架手册(Gitbook): PHP-MSF开发手册

API Document(Rawgit): 类文档

示例DEMO项目: PHP-MSF DEMO

帮助完善文档: https://github.com/pinguo/php-msf-docs,请提交PR。

交流与反馈

PHP-MSF#1群(QQ): 614054288

快速起步

$>php -r "copy('https://raw.githubusercontent.com/pinguo/php-msf-docker/master/installer.php', 'installer.php');include('installer.php');" && source ~/.bashrc

installer.php会检查运行环境,根据你的自定义配置,自动创建项目模板,composer安装依赖,启动服务。如果cdn.rawgit.com无法访问,可以直接克隆或者下载php-msf-docker,提取installer.php,然后直接运行php installer.php

如果一切顺利,运行到最后你将看到如下的输出:

[2017-09-06 16:08:34] Run composer install success
[2017-09-06 16:08:34] Congratulations, all are installed successfully!
[2017-09-06 16:08:34] You can, visit http://127.0.0.1:8990/Welcome for test
      _______                               ____
________  / /_  ____        ____ ___  _____/ __/
___/ __ \/ __ \/ __ \______/ __ `__ \/ ___/ /_
__/ /_/ / / / / /_/ /_____/ / / / / (__  ) __/
_/ .___/_/ /_/ .___/     /_/ /_/ /_/____/_/
/_/         /_/         Camera360 Open Source TM
[2017-09-06 16:08:34] Swoole  Version: 1.9.18
[2017-09-06 16:08:34] PHP     Version: 7.1.8
[2017-09-06 16:08:34] Application ENV: docker
[2017-09-06 16:08:34] Listen     Addr: 0.0.0.0
[2017-09-06 16:08:34] Listen     Port: 8990

访问测试:

$>curl http://127.0.0.1:8990/Welcome
hello world!

注意端口,如果你不是8990,你需要修改,然后访问测试。

标准应用结构

├── app // PHP业务代码
│   ├── AppServer.php // 应用server类,可根据需求自定义
│   ├── Controllers // 控制器类目录
│   ├── Lib // 特殊逻辑处理类目录
│   ├── Models // Model类目录
│   ├── Route // 特殊路由规则类目录
│   ├── Tasks // Task类目录
│   └── Views // 视图文件目录
├── build.sh // 构建脚本(拉取docker镜像,启动容器)
├── checkstyle.sh // 代码检查脚本
├── composer.json // composer包依赖配置文件
├── config // 配置目录
├── server.php // server启动脚本
├── console.php // 命令行脚本
├── test // 单元测试目录

上述为基于php-msf的标准应用结构,一键安装程序installer.php会自动生成目录,用户可以根据需求创建一些自定义目录,只要符合psr4标准即可自动加载。

服务启动

调试模式

$>./server.php start

Daemon模式

$>./server.php start -d

停止服务

$>./server.php stop

重启服务

$>./server.php restart

Docker

我们制作了Docker镜像,方便Docker用户快速的安装环境,运行PHP-MSF DEMO工程。另外期望在开发环境修改代码实时预览效果,建议使用Docker for Mac/Windows桌面版。

如果是升级Docker,它会自动迁移原有的镜像和容器,请耐心等待,千万不能中途kill掉Docker进程,否则再想迁移就难了。

Docker Registry(阿里云):

  • 公网地址: docker pull registry.cn-hangzhou.aliyuncs.com/pinguo-ops/php-msf-docker:latest
  • 经典内网: docker pull registry-internal.cn-hangzhou.aliyuncs.com/pinguo-ops/php-msf-docker:latest
  • VPC网络: docker pull registry-vpc.cn-hangzhou.aliyuncs.com/pinguo-ops/php-msf-docker:latest
  • DockerHub(国外): docker pull pinguoops/php-msf-docker

框架定位

我们专注打造稳定高性能纯异步基于HTTP的微服务框架,作为nginx+php-fpm的替代技术栈实现架构的微服务化;而Tcp/WebSocket Server将作为插件的形势支持,或者作为其他独立的开源项目。

对于小型团队或者业务系统我们建议还是采用传统的nginx+php-fpm技术栈,对于成本和性能来说没有瓶颈,也就完全没有必要引入全新的技术栈。

对于大中型团队或者业务系统,处在服务治理或者服务化演进的重要阶段,php-msf是可选方案之一。

对于庞大的PHP应用集群,想要大幅节约服务器成本,提升服务性能,php-msf是可选方案之一。

对于聚合服务,比如大型的网站首页,想要通过服务器端聚合内容整合数据,php-msf是可选方案之一。

手工安装

推荐安装方式,通过编辑项目composer.json加入依赖pinguo/php-msf

{
    "require": {
        "pinguo/php-msf": ">=3.0.0"
    },
    "minimum-stability": "dev"
}

"minimum-stability": "dev"这个配置选项必须加上,因为日志组件依赖"monolog/monolog": "2.0.x-dev",并且monolog/monolog无2.0的release包,不过我们在生产环境已经验证其稳定性。

项目原则

稳定

php-msf经受了Camera360社区服务大流量、高并发的洗礼,稳定性得到充分验证。稳定性是我们花了大量时间、精力去解决的最重要问题,是三大原则的最重要原则。

高性能

IO密集性业务的单机处理能力提升5-10倍,这是生产环境中得出的真实数据,如Camera360社区某聚合服务在流量高峰需要40台服务器抗住流量,而采用php-msf重构之后只需要4台相同配置的服务器就可以抗住所有流量。

简单

由于Swoole复杂的进程模型,并且有同步阻塞和异步非阻塞之分,所以在运行相同代码逻辑时,可能在调用方式、传递参数都不一致,从而直线拉高了学习成本,我们为了屏蔽低层的差异,做了大量的工作,实现和传统MVC框架的唯一区别在于添加“yield”关键字。我们参考了Yii2框架的部分代码实践,我们期望无缝的从Yii2开发切换过来。

上述三大原则,是我们在新增特性、功能实现时,投票或者合并代码的依据,任何影响这些原则的PR也将会被拒绝。

关于协程

目前社区有几个PHP开源项目支持协程,它们大多采用Generator+Yield来实现,但是实现的细微差别会导致性能相差甚远,我们应该认识到协程能够以同步的代码书写方式而运行异步逻辑,故协程调度器的性能一定要足够的高,php-msf的协程调度性能是原生异步回调方式的80%,也就是说某个API采用原生异步回调写法QPS为10000,通过php-msf协程调度器调度QPS为8000。

为什么是微服务框架?

目前php-msf还在起步阶段,我们花了大量的时间和精力解决稳定性、高性能、内存问题,因为我们认为“基石”是“万丈高楼”的最基本的保障,只有基础打得牢,才能将“大楼”建设得“更高”。3.0版本是我们开源的起始版本,是我们迈出的重要一步,接下来我们重点会是分布式微服务框架的打磨。

另外,由于基于PHP常驻进程,并直接解析HTTP或者TCP请求,这是服务化最重要的支撑,基于此我们可以做很多原来不敢去实现的想法,总之想像空间很大。

感谢

php-msf最开始基于SwooleDistributed-1.7.x开发,而此次开源版本中,连接池主要采用了SD的实现。由于我们框架定位、解决的业务场景、稳定性的要求、代码风格等差异太大,因此我们决定自主研发微服务框架,每个框架都有自己的特色和优点,选择合适自己公司和业务场景的框架最重要,同时在此也感谢白猫;另外,在研发php-msf框架及生产环境应用过程中,遇到很多底层问题,不过都一一解决,而这些问题能够解决最重要就是Swoole开源项目创始人韩天峰-Rango的大力支持,在此深表感谢。

License

GNU General Public License, version 2 see https://www.gnu.org/licenses/gpl-2.0.html

php 获取文件哈希值(Hash)

HASH是根据文件的内容的数据通过逻辑运算得到的数值, 不同的文件(即使是相同的文件名)得到的HASH值是不同的, 所以HASH值就成了每一个文件在EMULE里的身份证. 不同HASH值的文件在EMULE里被认为是不同的文件,相同的HASH值的文件的内容肯定是完全相同(即使文件名不同). HASH值还有文件校验的功能,相当于文件的校验码. 所以还可以用来检查文件下载是否正确(所以EMULE下载完毕时,都会在HASH文件一遍, 检查文件是否出错)。

文件的Hash值在下载文件的校验方面有很大的用途,Hash值是文件内容的通过二进制码进行一系列的变换生成出来的,即使文件名发生变化,Hash的值也不会发生改变。因此在开发的过程中一般比较两个文件是否相同都是去比较两个文件的Hash值。

先上代码,后面解释。


运行结果:

d67d1c2dce86470f8fd81c7154d4999f

hash_file
hash_file — 使用给定文件的内容生成哈希值 。环境要求PHP 5 >= 5.1.2, PHP 7, PECL hash >= 1.1

说明
string hash_file ( string$algo , string$filename [, bool$raw_output = false ] )

参数
algo 要使用的哈希算法的名称,例如:”md5″,”sha256″,”haval160,4″ 等。

filename 要进行哈希运算的文件路径。支持 fopen 封装器。

raw_output设置为 TRUE,输出格式为原始的二进制数据。 设置为 FALSE,输出小写的 16 进制字符串。

返回值
如果 raw_output 设置为 TRUE, 则返回原始二进制数据表示的信息摘要, 否则返回 16 进制小写字符串格式表示的信息摘要。

拓展
md5_file() – 计算指定文件的 MD5 散列值

sha1_file() – 计算文件的 sha1 散列值

地址:http://demo.i5code.com/index.php?a=shows&catid=4&id=44

声明:本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。

QueryList 简单、 灵活、强大的PHP采集工具,让采集更简单一点。 phpquery

How to use?

试试更优雅的采集方式吧!

QueryList的出现让PHP做采集从未如此简单。得益于phpQuery,让使用QueryList几乎没有任何学习成本,只要会CSS3选择器就可以轻松使用QueryList了,和jQuery选择器用法完全通用,它让PHP做采集像jQuery选择元素一样简单。

初探

看看PHP用QueryList做采集到底有多简洁吧!

array(‘jQuery选择器’,’要采集的属性’),
‘image’ => array(‘img’,’src’)
))->data;
//打印结果
print_r($data);

//采集某页面所有的超链接
//可以先手动获取要采集的页面源码
$html = file_get_contents(‘http://cms.querylist.cc/google/list_1.html’);
//然后可以把页面源码或者HTML片段传给QueryList
$data = QueryList::Query($html,array(
‘link’ => array(‘a’,’href’)
))->data;
//打印结果
print_r($data);

/**
* 在线测试采集并查看采集结果:http://querylist.cc/page-Querytest.html
*/
进阶

上面的采集结果有很多“杂质”,一定不会满足你的要求,来获取我们真正想要的结果。

array(‘.post_content img’,’src’)
))->data;
//打印结果
print_r($data);

//采集该页面文章列表中所有[文章]的超链接
$data = QueryList::Query(‘http://cms.querylist.cc/google/list_1.html’,array(
‘link’ => array(‘h2>a’,’href’,”,function($content){
//利用回调函数补全相对链接
$baseUrl = ‘http://cms.querylist.cc’;
return $baseUrl.$content;
})),’.cate_list li’)->data;
//打印结果
print_r($data);
全貌

正如你看到的那样,QueryList只有一个主要的方法Query,学会了使用Query方法也就意味着你已经熟练了QueryList!

array(‘h1′,’text’),
//采集文章发布日期,这里用到了QueryList的过滤功能,过滤掉span标签和a标签
‘date’ => array(‘.pt_info’,’text’,’-span -a’,function($content){
//用回调函数进一步过滤出日期
$arr = explode(‘ ‘,$content);
return $arr[0];
}),
//采集文章正文内容,利用过滤功能去掉文章中的超链接,但保留超链接的文字,并去掉版权、JS代码等无用信息
‘content’ => array(‘.post_content’,’html’,’a -.content_copyright -script’,function($content){
//利用回调函数下载文章中的图片并替换图片路径为本地路径
//使用本例请确保当前目录下有image文件夹,并有写入权限
//由于QueryList是基于phpQuery的,所以可以随时随地使用phpQuery,当然在这里也可以使用正则或者其它方式达到同样的目的
$doc = phpQuery::newDocumentHTML($content);
$imgs = pq($doc)->find(‘img’);
foreach ($imgs as $img) {
$src = ‘http://cms.querylist.cc’.pq($img)->attr(‘src’);
$localSrc = ‘image/’.md5($src).’.jpg’;
$stream = file_get_contents($src);
file_put_contents($localSrc,$stream);
pq($img)->attr(‘src’,$localSrc);
}
return $doc->htmlOuter();
})
);
$rang = ‘.content’;
$ql = QueryList::Query($page,$reg,$rang);
$data = $ql->getData();
//打印结果
print_r($data);
扩展

QueryList的功能很单一,就是Query,但扩展让QueryList变得无限可能!

‘http://cms.querylist.cc/news/list_2.html’,
‘referrer’=>’http://cms.querylist.cc’,
‘method’ => ‘GET’,
‘params’ => [‘var1’ => ‘testvalue’, ‘var2’ => ‘somevalue’],
‘user_agent’=>’Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:21.0) Gecko/20100101 Firefox/21.0’,
‘cookiePath’ => ‘./cookie.txt’,
‘timeout’ =>’30’
])->setQuery([‘link’ => [‘h2>a’,’href’,”,function($content){
//利用回调函数补全相对链接
$baseUrl = ‘http://cms.querylist.cc’;
return $baseUrl.$content;
}]],’.cate_list li’)->getData(function($item){
return $item[‘link’];
});

//多线程扩展
QueryList::run(‘Multi’,[
‘list’ => $urls,
‘curl’ => [
‘opt’ => array(
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_AUTOREFERER => true,
),
//设置线程数
‘maxThread’ => 100,
//设置最大尝试数
‘maxTry’ => 3
],
‘success’ => function($a){
//采集规则
$reg = array(
//采集文章标题
‘title’ => array(‘h1′,’text’),
//采集文章发布日期,这里用到了QueryList的过滤功能,过滤掉span标签和a标签
‘date’ => array(‘.pt_info’,’text’,’-span -a’,function($content){
//用回调函数进一步过滤出日期
$arr = explode(‘ ‘,$content);
return $arr[0];
}),
//采集文章正文内容,利用过滤功能去掉文章中的超链接,但保留超链接的文字,并去掉版权、JS代码等无用信息
‘content’ => array(‘.post_content’,’html’,’a -.content_copyright -script’,function($content){
//利用回调函数下载文章中的图片并替换图片路径为本地路径
//使用本例请确保当前目录下有image文件夹,并有写入权限
//由于QueryList是基于phpQuery的,所以可以随时随地使用phpQuery,当然在这里也可以使用正则或者其它方式达到同样的目的
$doc = phpQuery::newDocumentHTML($content);
$imgs = pq($doc)->find(‘img’);
foreach ($imgs as $img) {
$src = pq($img)->attr(‘src’);
$localSrc = ‘image/’.md5($src).’.jpg’;
$stream = file_get_contents($src);
file_put_contents($localSrc,$stream);
pq($img)->attr(‘src’,$localSrc);
}
return $doc->htmlOuter();
})
);
$rang = ‘.content’;
$ql = QueryList::Query($a[‘content’],$reg,$rang);
$data = $ql->getData();
//打印结果,实际操作中这里应该做入数据库操作
print_r($data);
}
]);

https://querylist.cc/

微信支付宝支付接口

开发了多次支付宝与微信支付后,很自然产生一种反感,惰性又来了,想在网上找相关的轮子,可是一直没有找到一款自己觉得逞心如意的,要么使用起来太难理解,要么文件结构太杂乱,只有自己撸起袖子干了。

欢迎 Star,欢迎 PR!

特点

  • 命名不那么乱七八糟
  • 隐藏开发者不需要关注的细节
  • 根据支付宝、微信最新 API 开发而成
  • 高度抽象的类,免去各种拼json与xml的痛苦
  • 符合 PSR 标准,你可以各种方便的与你的框架集成
  • 文件结构清晰易理解,可以随心所欲添加本项目中没有的支付网关
  • 方法使用更优雅,不必再去研究那些奇怪的的方法名或者类名是做啥用的

运行环境

  • PHP 7.0+
  • composer

支持的支付网关

由于各支付网关参差不齐,所以我们抽象了两个方法 driver()gateway()

两个方法的作用如下:

driver() : 确定支付平台,如 alipay,wechat;

gateway(): 确定支付网关。通过此方法,确定支付平台下的支付网关。例如,支付宝下有 「电脑网站支付」,「手机网站支付」,「APP 支付」三种支付网关,通过传入 web,wap,app 确定。

详细思路可以查看源代码。

1、支付宝

  • 电脑支付
  • 手机网站支付
  • APP 支付

SDK 中对应的 driver 和 gateway 如下表所示:

driver gateway 描述
alipay web 电脑支付
alipay wap 手机网站支付
alipay app APP 支付

2、微信

  • 公众号支付
  • 小程序支付
  • H5 支付
  • 扫码支付
  • 刷卡支付

SDK 中对应的 driver 和 gateway 如下表所示:

driver gateway 描述
wechat mp 公众号支付
wechat miniapp 小程序支付
wechat wap H5 支付
wechat scan 扫码支付
wechat pos 刷卡支付

支持的方法

所有网关均支持以下方法

  • pay (支付接口)
  • refund (退款接口)
  • close (关闭订单接口)
  • verify (验证服务器返回消息是否合法)

安装

composer required yansongda/pay

使用说明

0、一个完整的例子:

use Yansongda\Pay\Pay;

$config = [
    'alipay' => [
        'app_id' => '2016082000295641',
        'notify_url' => 'http://yansongda.cn/alipay_notify.php',
        'return_url' => 'http://yansongda.cn/return.php',
        'ali_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuWJKrQ6SWvS6niI+4vEVZiYfjkCfLQfoFI2nCp9ZLDS42QtiL4Ccyx8scgc3nhVwmVRte8f57TFvGhvJD0upT4O5O/lRxmTjechXAorirVdAODpOu0mFfQV9y/T9o9hHnU+VmO5spoVb3umqpq6D/Pt8p25Yk852/w01VTIczrXC4QlrbOEe3sr1E9auoC7rgYjjCO6lZUIDjX/oBmNXZxhRDrYx4Yf5X7y8FRBFvygIE2FgxV4Yw+SL3QAa2m5MLcbusJpxOml9YVQfP8iSurx41PvvXUMo49JG3BDVernaCYXQCoUJv9fJwbnfZd7J5YByC+5KM4sblJTq7bXZWQIDAQAB',
        'private_key' => 'MIIEpAIBAAKCAQEAs6+F2leOgOrvj9jTeDhb5q46GewOjqLBlGSs/bVL4Z3fMr3p+Q1Tux/6uogeVi/eHd84xvQdfpZ87A1SfoWnEGH5z15yorccxSOwWUI+q8gz51IWqjgZxhWKe31BxNZ+prnQpyeMBtE25fXp5nQZ/pftgePyUUvUZRcAUisswntobDQKbwx28VCXw5XB2A+lvYEvxmMv/QexYjwKK4M54j435TuC3UctZbnuynSPpOmCu45ZhEYXd4YMsGMdZE5/077ZU1aU7wx/gk07PiHImEOCDkzqsFo0Buc/knGcdOiUDvm2hn2y1XvwjyFOThsqCsQYi4JmwZdRa8kvOf57nwIDAQABAoIBAQCw5QCqln4VTrTvcW+msB1ReX57nJgsNfDLbV2dG8mLYQemBa9833DqDK6iynTLNq69y88ylose33o2TVtEccGp8Dqluv6yUAED14G6LexS43KtrXPgugAtsXE253ZDGUNwUggnN1i0MW2RcMqHdQ9ORDWvJUCeZj/AEafgPN8AyiLrZeL07jJz/uaRfAuNqkImCVIarKUX3HBCjl9TpuoMjcMhz/MsOmQ0agtCatO1eoH1sqv5Odvxb1i59c8Hvq/mGEXyRuoiDo05SE6IyXYXr84/Nf2xvVNHNQA6kTckj8shSi+HGM4mO1Y4Pbb7XcnxNkT0Inn6oJMSiy56P+CpAoGBAO1O+5FE1ZuVGuLb48cY+0lHCD+nhSBd66B5FrxgPYCkFOQWR7pWyfNDBlmO3SSooQ8TQXA25blrkDxzOAEGX57EPiipXr/hy5e+WNoukpy09rsO1TMsvC+v0FXLvZ+TIAkqfnYBgaT56ku7yZ8aFGMwdCPL7WJYAwUIcZX8wZ3dAoGBAMHWplAqhe4bfkGOEEpfs6VvEQxCqYMYVyR65K0rI1LiDZn6Ij8fdVtwMjGKFSZZTspmsqnbbuCE/VTyDzF4NpAxdm3cBtZACv1Lpu2Om+aTzhK2PI6WTDVTKAJBYegXaahBCqVbSxieR62IWtmOMjggTtAKWZ1P5LQcRwdkaB2rAoGAWnAPT318Kp7YcDx8whOzMGnxqtCc24jvk2iSUZgb2Dqv+3zCOTF6JUsV0Guxu5bISoZ8GdfSFKf5gBAo97sGFeuUBMsHYPkcLehM1FmLZk1Q+ljcx3P1A/ds3kWXLolTXCrlpvNMBSN5NwOKAyhdPK/qkvnUrfX8sJ5XK2H4J8ECgYAGIZ0HIiE0Y+g9eJnpUFelXvsCEUW9YNK4065SD/BBGedmPHRC3OLgbo8X5A9BNEf6vP7fwpIiRfKhcjqqzOuk6fueA/yvYD04v+Da2MzzoS8+hkcqF3T3pta4I4tORRdRfCUzD80zTSZlRc/h286Y2eTETd+By1onnFFe2X01mwKBgQDaxo4PBcLL2OyVT5DoXiIdTCJ8KNZL9+kV1aiBuOWxnRgkDjPngslzNa1bK+klGgJNYDbQqohKNn1HeFX3mYNfCUpuSnD2Yag53Dd/1DLO+NxzwvTu4D6DCUnMMMBVaF42ig31Bs0jI3JQZVqeeFzSET8fkoFopJf3G6UXlrIEAQ==',
    ],
];

$config_biz = [
    'out_trade_no' => '1',
    'total_amount' => '1',
    'subject' => 'test subject',
];

$pay = new Pay($config);
return $pay->dirver('alipay')->gateway('web')->pay($config_biz);

1、准备配置参数

$config = [
    'alipay' => [
        'app_id' => '',             // 支付宝提供的 APP_ID
        'ali_public_key' => '',     // 支付宝公钥,1行填写
        'private_key' => '',        // 自己的私钥,1行填写
    ],
];
$config_biz = [
    'out_trade_no' => '12',                 // 订单号
    'total_amount' => '13',                 // 订单金额,单位:元
    'subject' => 'test subject',   // 订单商品标题
];

2、在代码中使用

$pay = new Pay($config);
return $pay->dirver('alipay')->gateway('web')->pay($config_biz);

返回值

pay (支付接口)

返回 跳转到支付宝进行支付的 Html 代码。在正式项目中,直接 return 即可。

refund (退款接口)

true/false

close (关闭订单接口)

true/false

错误详情

使用非跳转接口(如, refund 接口,close 接口)时,如果有错误产生,会抛出 GatewayException 错误。

各个支付网关配置说明

由于支付网关不同,每家参数参差不齐,为了方便,我们抽象定义了两个参数:$config,$config_biz,分别为全局参数,业务参数。但是,所有配置参数均为官方标准参数,无任何差别。

「业务参数」为订单相关的参数,「全局参数」为除订单相关参数以外的全局性参数。

具体参数列表请查看每个支付网关的使用说明。

1、支付宝 – 电脑网站支付

最小配置参数

$config = [
    'alipay' => [
        'app_id' => '',             // 支付宝提供的 APP_ID
        'ali_public_key' => '',     // 支付宝公钥,1行填写
        'private_key' => '',        // 自己的私钥,1行填写
    ],
];
$config_biz = [
    'out_trade_no' => '12',                 // 订单号
    'total_amount' => '13',                 // 订单金额,单位:元
    'subject' => 'test subject',   // 订单商品标题
];

所有配置参数

所有参数均为官方标准参数,无任何差别。点击这里 查看官方文档。

$config = [
    'alipay' => [
        'app_id' => '',             // 支付宝提供的 APP_ID
        'ali_public_key' => '',     // 支付宝公钥,1行填写
        'private_key' => '',        // 自己的私钥,1行填写
        'return_url' => '',         // 同步通知 url,*强烈建议加上本参数*
        'notify_url' => '',         // 异步通知 url,*强烈建议加上本参数*
    ],
];
$config_biz = [
    'out_trade_no' => '',
    'total_amount' => '',                 
    'subject' => '',

    // 订单描述
    'body' => '',

    // 订单包含的商品列表信息,Json格式: {"show_url":"https://或http://打头的商品的展示地址"} ,在支付时,可点击商品名称跳转到该地址      
    'goods_detail' => '',

    // 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 该参数数值不接受小数点, 如 1.5h,可转换为 90m。该参数在请求到支付宝时开始计时。
    'timeout_express' => '',                
    
    // 禁用渠道,用户不可用指定渠道支付当有多个渠道时用“,”分隔注:与enable_pay_channels互斥
    'disable_pay_channels' => '',

    // 可用渠道,用户只能在指定渠道范围内支付当有多个渠道时用“,”分隔注:与disable_pay_channels互斥
    'enable_pay_channels' => '',

    // 公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。支付宝只会在异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝
    'passback_params' => '',

    // 业务扩展参数,详见 [业务扩展参数说明](https://docs.open.alipay.com/#kzcs)
    'extend_params' => '',

    // 商品主类型:0—虚拟类商品,1—实物类商品(默认)注:虚拟类商品不支持使用花呗渠道
    'goods_type' => '',

    // 获取用户授权信息,可实现如免登功能。获取方法请查阅:用户信息授权
    'auth_token' => '',

    /**
     *  PC扫码支付的方式,支持前置模式和跳转模式。
     *
     *  前置模式是将二维码前置到商户的订单确认页的模式。需要商户在自己的页面中以iframe方式请求支付宝页面。具体分为以下几种:
     *  0:订单码-简约前置模式,对应iframe宽度不能小于600px,高度不能小于300px;
     *  1:订单码-前置模式,对应iframe宽度不能小于300px,高度不能小于600px;
     *  3:订单码-迷你前置模式,对应iframe宽度不能小于75px,高度不能小于75px;
     *  4:订单码-可定义宽度的嵌入式二维码,商户可根据需要设定二维码的大小。
     *
     *  跳转模式下,用户的扫码界面是由支付宝生成的,不在商户的域名下。
     *  2:订单码-跳转模式
     */
    'qr_pay_mode' => '',

    // 商户自定义二维码宽度 注:qr_pay_mode=4时该参数生效
    'qrcode_width' => ''
];

2、支付宝 – 手机网站支付

最小配置参数

$config = [
    'alipay' => [
        'app_id' => '',             // 支付宝提供的 APP_ID
        'ali_public_key' => '',     // 支付宝公钥,1行填写
        'private_key' => '',        // 自己的私钥,1行填写
    ],
];
$config_biz = [
    'out_trade_no' => '12',                 // 订单号
    'total_amount' => '13',                 // 订单金额,单位:元
    'subject' => 'test subject',   // 订单商品标题
];

所有配置参数

该网关大部分参数和 「电脑支付」 相同,具体请参考 官方文档

3、支付宝 – APP 支付

最小配置参数

$config = [
    'alipay' => [
        'app_id' => '',             // 支付宝提供的 APP_ID
        'notify_url' => '',         // 支付宝异步通知地址
        'ali_public_key' => '',     // 支付宝公钥,1行填写
        'private_key' => '',        // 自己的私钥,1行填写
    ],
];
$config_biz = [
    'out_trade_no' => '12',                 // 订单号
    'total_amount' => '13',                 // 订单金额,单位:元
    'subject' => 'test subject',   // 订单商品标题
];

所有配置参数

该网关大部分参数和 「电脑支付」 相同,具体请参考 官方文档

4、微信 – 公众号支付

最小配置参数

TODO

所有配置参数

所有参数均为官方标准参数,无任何差别。点击这里 查看官方文档。

5、微信 – 小程序支付

由于「小程序支付」和「公众号支付」都使用的是 JSAPI,所以,该网关大部分参数和 「公众号支付」 相同。

6、微信 – H5 支付

TODO

7、微信 – 扫码支付

TODO

8、微信 – 刷卡支付

TODO

代码贡献

由于测试及使用环境的限制,本项目中只开发了「支付宝」和「微信支付」的相关支付网关。

如果您有其它支付网关的需求,或者发现本项目中需要改进的代码,欢迎 Fork 并提交 PR!

LICENSE

 

转自 https://git.oschina.net/yansongda/pay

帝国CMS统一修改已添加内容页存放目录修改成自定义的方法

今天电脑技术吧为大家带来一篇文章:“帝国CMS统一修改已添加内容页存放目录修改成自定义的方法”,

原始的地址截图:有个日期目录,要把这个日期目录“干掉”帝国CMS统一修改已添加内容页存放目录修改成自定义的方法

后台设置栏目:

blob.png

设置完毕以后,在后台–系统设置–数据更新–更新信息页地址  重新生成一次信息页地址。这样就可以了!如下图

blob.png

不过你会发现 新加入的信息,地址已经变成“目录/ID编号.html”这个方式了!但是从前添加的信息还是“目录/年月日/ID编号.html”这个方式!那么请看下面的教程:

使用帝国CMS按默认设置建立目录后,突然意识到“目录/年月日/ID编号.html”的生成方式不如“目录/ID编号.html”生成方式。此方式对搜索引擎更友好。

于是立即行动,修改后却发现新更新的内容将采用此种方式,却发现修改生成方式之前的文件还是固执地按照以前方式生成。

仔细研究了下帝国数据库表结构,发现newspath字段记录的是文件路径,这样在数据库里把这个字段清空就行了!

解决办法(建议一条条执行):

update 表名  set newspath=”;

如何执行?从哪里执行?请看下图

blob.png

实例使用(新闻、文章):

update phome_ecms_news set newspath=”;

update phome_ecms_article set newspath=”;

如何知道表名:请看下图(红色框内就是表名,栏目里使用了哪个表就执行哪个表就行了)

161253bdmezmd8a44g5aet.png

操作完成以后,已添加内容页的地址 就变成目录/ID编号.html”这个方式了!(记得 在后台–系统设置–数据更新–更新信息页地址 ,然后数据更新里更新首页 栏目页 内容页)

做完后的地址形式截图:

帝国CMS统一修改已添加内容页存放目录修改成自定义的方法

http://www.zhishiwu.com/zhanzhang/rumen/32770.html

php 将远程二维码 画到海报上面

public function createimg($logo,$filename,$QR=”images/code.jpg”)
{
header(“Content-type: image/jpg”);
// $QR=”images/code.jpg”;
if ($logo !== FALSE) {
$QR = imagecreatefromstring(file_get_contents($QR));

$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_URL, $logo);
curl_setopt($ch, CURLOPT_REFERER, $logo);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$result = curl_exec($ch);
curl_close($ch);
$logo = imagecreatefromstring($result);
$QR_width = imagesx($QR);//二维码图片宽度
$QR_height = imagesy($QR);//二维码图片高度
$logo_width = imagesx($logo);//logo图片宽度
$logo_height = imagesy($logo);//logo图片高度
$logo_qr_width = $QR_width / 5;
$scale = $logo_width/$logo_qr_width;
$logo_qr_height = $logo_height/$scale;
$from_width = ($QR_width – $logo_qr_width) / 2;
imagecopyresampled($QR, $logo, 250, 920, 0, 0, 300, 300, $logo_width, $logo_height);
}
imagejpeg($QR,$filename,50);

}

php 操作 redis 之 string 字符串类型

<?php
//第一步:实例化redis对象
$redis = new redis();
//第二步:php客户端设置的ip及端口
$redis->connect(“127.0.0.1″,”6379”);
//第三部:配置连接密码 检测redis服务器连接状态

//连接失败直接结束 并输出
$auth = $redis->auth(‘zhenai’) or die(“redis 服务器连接失败”);
// var_dump($auth);连接成功 返回 true 反之 返回false
//第四步 可用可不用
// echo $connect_status=$redis->ping();
// if($connect_status===”+PONG”)
// {
// echo “redis 服务器连接成功”;
// }
//就是如此简单
//var_dump($connect_status);
/*
第一部分
字符串(String)
一个键最多存储512MB
1》SET:设置key对应的值为value

语法:SET key value [EX seconds] [PX milliseconds] [NX|XX]
EX seconds:设置键的key的过期时间SET key value EX seconds — SETEX
PX milliseconds:以毫秒的形式设置过期时间SET key value PX milliseconds–PSETEX
NX:只有键不存在的时候才可以设置成功SET key value NX–SETNX
XX:只有key已经存在的时候才可以设置
*/
//1.存储一个值(set) 获取一个值(get)
$redis->set(“goonls”,”com”);
$goonls=$redis->get(“goonls”);
// echo $goonls;

//2.存储多个值(mset) 获取多个值(mget 或 getMultiple)
$array = array(
‘first_key’=>’first_val’,
‘second_key’=>’second_val’,
‘third_key’=>’third_val’);
$array_get = array(‘first_key’,’second_key’,’third_key’);
$redis->mset($array);
$goonls_array=$redis->mget($array_get);
// print_r($goonls_array);
/*打印结果 */
// Array
// (
// [0] => first_val
// [1] => second_val
// [2] => third_val
// )

//3.取一个不存在的值 返回false
$non_existent_string=$redis->get(“goonls.com”);
// var_dump($non_existent_string);
//返回false

//4.设置一个已经存在的值 会覆盖原来的值
$redis->set(“goonls”,”goonls.com”);
$set_existent_string=$redis->get(‘goonls’);
//echo $set_existent_string;
//覆盖原来“com”值 为 “goonls.com”
//5.删除 delete
// 描述:删除指定的键
// 参数:一个键,或不确定数目的参数,每一个关键的数组:key1 key2 key3 … keyN
// 返回值:删除的项数
$redis->delete(“goonls”);
$set_existent_string=$redis->get(‘goonls’);
//var_dump($set_existent_string);
//结果:bool(false)
// 6,setnx
// 描述:如果在数据库中不存在该键,设置关键值参数
// 参数:key value
// 返回值:BOOL 成功返回:TRUE;失败返回:FALSE

$set_string=$redis->set(“goonls.com”,’11111′);
//上面已经设置 goonls.com 的 值
// 下面重复设置将返回false
$setnx_string=$redis->setnx(“goonls.com”,’22222′);
//var_dump($setnx_string);//false
// 7,exists

// 描述:验证指定的键是否存在
// 参数key
// 返回值:Bool 成功返回:TRUE;失败返回:FALSE

$set_string=$redis->set(“goonls.com7”,’11111′);
//var_dump($redis->exists(‘goonls.com7′)); //结果:bool(true)

//8.GETSET

// 描述:先获取值 然后设置值
// 参数key value
// 返回值:Bool 成功返回:value;失败返回:FALSE

$set_string=$redis->set(“goonls.com8”,’GETSET’);

$goonls_com8=$redis->getset(‘goonls.com8′,’GETSET26’);
//var_dump($goonls_com8); //返回 GETSET
//echo $redis->get(‘goonls.com8’);
//9.incr

// 描述:数字递增存储键值键.
// 参数:key value:将被添加到键的值
// 返回值:INT the new value
// 实例:

if(!$redis->exists(‘goonls.com9’))
{
$redis->set(‘goonls.com9’,1);
}
else
{
$redis->incr(‘goonls.com9’);
}
echo $redis->get(‘goonls.com9’);

// 10,exists

// 描述:验证指定的键是否存在
// 参数key
// 返回值:Bool 成功返回:TRUE;失败返回:FALSE
// 范例:

// 复制代码 代码如下:

$redis->set(‘test’,’1111111111111′);
var_dump($redis->exists(‘test’)); //结果:bool(true)

?>