标签 php 下的文章

做了一点微小的工作

还记得很久之前,给 Blog 做过一次优化,从 Wordpress 迁移到 Typecho,同时上了 HTTPS,然后为了加快页面加载速度,给全站的静态资源加上了七牛的 CDN。不过由于服务器在境外,用的又是 DigitalOcean 最低配的 VPS,访问速度还是很慢。

现在又有提升性能的机会啦,PHP7 在去年年底的时候正式发布,本来想第一时间升级,不过想了想刚发布的版本大大小小都会有 Bug,还是不做小白鼠了,还是等等吧,结果一拖就是一年。。好吧,是我懒。

今天折腾了快一个下午的时间,不光是升级了 PHP,还做了其他的升级和优化。

  • 升级了 VPS 套餐
  • 重新部署了startssl 家的 HTTPS 证书(毕竟免费..)
  • 升级 Nginx 到1.10.2
  • 升级 PHP 到 7.0.13

为了达到 PHP7 的最佳性能,可参考鸟哥的博客 http://www.laruence.com/2015/12/04/3086.html。HTTPS也可以做很多配置优化可参考 http://op.baidu.com/2015/04/https-s01a03。比较幸运,升级基本上比较顺利,Typecho 对 PHP7 完全兼容,唯一需要修改的是将配置中数据库的适配器改为 PDO 即可,因为 PHP7 已经不支持最原始的 MySQL 扩展了。

最终升级完成之后,还是很有效果的。虽然没有做测试,但从肉眼看,等待时间明显减少了:)

-- 2017-3-31 Update

  • 由于 StartCom 遭到了 Google 的 惩罚,导致证书不被 chrome 信任,于是使用 Let’s Encrypt 证书做了替换,并自动更新有效期,参考 acme-tiny
  • 将 VPS 由 NewYork 机房搬到了 SanFrancisco 机房,减少了路由节点跳数,不得不给 digitalocean 点个赞,全程自动化的操作简直太方便

-- 2017-05-12 Update

升级了 Openssl, 重新编译了 Nginx, 并做了如下优化:

  • 添加了对 HTTP2 的支持
  • 优化了 ssl ciphers 配置,修复已知的漏洞
  • 添加了 HSTS 特性,可减少一次 RTT
  • 添加了 fastopen 特性
  • 参考 mozilla 的推荐配置

关于 .user.ini 文件

昨天转移一个wp站点的时候遇到的一个小问题,绕了不少弯路,写出来分享下。

由于域名不变,按照官网的流程,只需要复制代码,转移数据库文件,在新的服务器配置一个虚拟主机,最后修改IP指向,就OK了。

但是事情总不会那么顺利,访问网站,提示No input file specified,这个错误引发的原因可能性很多,于是看了下nginx的error.log,发现如下错误:

2015/10/14 21:14:13 [error] 1832#0: *130022 FastCGI sent in stderr: "PHP message: PHP Warning:  Unknown: open_basedir restriction in effect. File(/home/****/***/index.php) is not within the allowed path(s): (/tmp/:/proc/) in Unknown on line 0
PHP message: PHP Warning:  Unknown: failed to open stream: Operation not permitted in Unknown on line 0
Unable to open primary script: /home/****/***/index.php (Operation not permitted)" while reading response header from upstream, client: 218.30.116.8, server: zhangrenqin.com, request: "GET /favicon.ico HTTP/1.1", upstream: "fastcgi://unix:/var/run/php5-fpm.sock:", host: "***", referrer: "***"

答案已经很明显了,由于wp使用了open_basedir这个函数,这个函数可以访问系统中的任何文件,安全隐患比较大,要对其进行限制,一般在php.ini里禁用或者限制在哪些目录使用。所以只要在php.ini文件中将当前网站的root目录加入到open_basedir的配置中即可,用:分割

open_basedir=/home/****/***/:/tmp/:/proc/

好了,现在访问就正常了。

这样添加的一个问题是全局生效,如果配置了多个虚拟主机,其中一个站点的权限被拿掉,其他站点也就跟着倒霉。.user.ini文件可以完美的解决这个问题,它能动态扩展php.ini。将上面的配置写入.user.ini,然后放到网站根目录即可。

php.ini的配置清单可参考官方文档:http://www.php.net/manual/en/ini.list.php
其中Changeable为PHP_INI_PERDIR、PHP_INI_USER、PHP_INI_ALL可以配置到.user.ini中,而PHP_INI_SYSTEM只能配置到php.ini。

可以从配置中看到,open_basedir选项在PHP > 5.2.3版本中已经被加入。

Name    Default    Changeable    Changelog
open_basedir    NULL    PHP_INI_ALL    PHP_INI_SYSTEM in PHP < 5.2.3.

PHP 通过 Thrift 操作 Hbase

Hbase1.0之后的版本中没有提供Thrift源码,需要到官网下载。地址

生成PHP文件:

thrift -gen php <PATH>/Hbase.thrift

启动Hbase和Hbase的Thrift守护程序

<PATH-TO-HBASE>/bin/start-hbase.sh
<PATH-TO-HBASE>/bin/hbase-daemon.sh start thrift

Thrift默认会监听9090端口,下面是一个常用操作的例子。命名为index.php,目录结构为:

gen-php/
lib/
index.php

Hbase里的数据结构如下,表名是blog,包含了articleauthor两个列族。

hbase(main):015:0> scan 'blog'
ROW          COLUMN+CELL
 1           column=article:content, timestamp=1434687037710, value=Hello,World
 1           column=article:tags, timestamp=1434533143824, value=Hadoop,HBase,NoSQL
 1           column=article:title, timestamp=1434533099302, value=Head First HBase
 1           column=author:name, timestamp=1434533489863, value=itsmikej1
 1           column=author:nickname, timestamp=1434687081713, value=\xE5\xB0\x8F\xE9\xBB\x91
1 row(s) in 0.0410 seconds

index.php

<?php
/**
 * Hbase操作
 *
 * @author itsmikej
 * @link https://mikej.me
 */

require_once __DIR__.'/lib/Thrift/ClassLoader/ThriftClassLoader.php';

use Thrift\ClassLoader\ThriftClassLoader;

$loader = new ThriftClassLoader();
$loader->registerNamespace('Thrift', __DIR__ . '/lib');
# $loader->registerDefinition("tService", realpath(__DIR__ . "/gen-php"));
$loader->register();

use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\TSocket;
use Thrift\Transport\TBufferedTransport;

require_once __DIR__ . "/gen-php/Hbase/Hbase.php";
require_once __DIR__ . "/gen-php/Hbase/Types.php";


try {
    $socket = new TSocket('127.0.0.1', '9090');
    $socket->setSendTimeout(10000);
    $socket->setRecvTimeout(20000);
    $transport = new TBufferedTransport($socket);
    $protocol = new TBinaryProtocol($transport);
    $client = new Hbase\HbaseClient($protocol);
    $transport->open();

    $tables = $client->getTableNames();
    $table = current($tables);

    $mutations = array();
    $rowKey = '2';
    $row = array(
        'article' => array(
            'content' => 'Hello,Thrift',
            'tags' => 'Hbase',
            'title' => 'Thrift Test'
        ),
        'author' => array(
            'name' => 'tom',
            'nickname' => '小白',
            'sex' => 'man'  # 列可以动态扩展
        )
    );

    foreach ($row as $column_family => $data) {
        foreach ($data as $qualifier => $content) {
            $info = array(
                'column' => $column_family. ':' .$qualifier,
                'value' => $content
            );
            $mutations[] = new \Hbase\Mutation($info);
        }
    }

    # 添加数据
    $client->mutateRow($table, $rowKey, $mutations, array());


    $rows = array(
        '3' => $row,
        '4' => $row,
    );
    $row['author']['sex'] = 'woman';
    $rows['5'] = $row;

    # 批量添加
    $batchRecord = array();
    foreach ($rows as $row_key => $row) {
        $mutations = array();
        foreach ($row as $column_family => $data) {

            foreach ($data as $qualifier => $content) {
                $info = array(
                    'column' => $column_family . ':' . $qualifier,
                    'value' => $content
                );
                $mutations[] = new \Hbase\Mutation($info);
            }
        }
        $batchRecord[] = new \Hbase\BatchMutation(array(
            'row' => $row_key,
            'mutations' => $mutations
        ));

    }
    $client->mutateRows($table, $batchRecord, array());


    # 删除数据
    $client->deleteAllRow($table, $rowKey, array());


    # 查询数据
    $ret = $client->getRow($table, $rowKey, array());
    var_dump($ret, $ret[0]->columns['author:name']->value);


    # 扫描数据
    $scan = $client->scannerOpenWithStop($table, 1, 3, array(), array());
    $ret = $client->scannerGetList($scan, 2);
    var_dump($ret);


    # 过滤器
    $filter = array();
    $filter[] = "SingleColumnValueFilter('author', 'name', =, 'binary:tom')";
    $filter[] = "SingleColumnValueFilter('author', 'sex', =, 'binary:woman')";
    $filterString = implode(" AND ", $filter);
    $scanFilter = new \Hbase\TScan();
    $scanFilter->filterString = $filterString;
    $scanFilter->startRow = 1;
    $scanFilter->stopRow = 6;
    # $scanFilter->columns = array('column' => 'author'); # 指定返回列族

    $scan = $client->scannerOpenWithScan($table, $scanFilter, array());
    $ret = $client->scannerGetList($scan, 2);
    var_dump($ret);

    $transport->close();

} catch (Exception $e) {
    var_dump($e->getMessage());
}

参考

SingleColumnValueFilter过滤器在手册里的说明参数位置是错误的,正确的应该为:

Syntax: SingleColumnValueFilter(‘<family>’, ‘<qualifier>’, <compare operator>, ‘<comparator>’, ‘<filterIfColumnMissing_boolean>, <latest_version_boolean>’)
Syntax: SingleColumnValueFilter(‘<family>’, ‘<qualifier>, <compare operator>, ‘<comparator>’)
Example: “SingleColumnValueFilter (‘FamilyA’, ‘Column1’, <=, ‘abc’,‘true, false’)”
Example: “SingleColumnValueFilter (‘FamilyA’, ‘Column1’, <=, ‘abc’)”

Moonrise 开发框架

简介

Moonrise是我用业余时间写的一个PHP框架,借鉴了其他很多框架的思想,比如CILaravel,当然使用了一些优秀第三方Composer包。为了能让框架尽快跑起来,很多细节处理得不够好,目前还在完善中。

Github地址:https://github.com/itsmikej/Moonrise。欢迎Fork和Pull Request

核心功能

  • MVC
  • 路由自动映射(支持web和cli)
  • 数据过滤
  • 异常和错误处理
  • 数据驱动支持Mysqli和PDO, 以及Memcache, MongoDB, Redis
  • ORM使用Active RecordEloquent也是可以的)
  • ......

环境要求

  • PHP >= 5.4.0
  • Composer(以及库中所需扩展)
  • Mysqli, PDO扩展

后记

  • Moonrise是因为小时候在夏天喜欢洗完澡躺在院子里的凉铺上边扇扇子边看星空和月亮直到睡着。。。