分类 PHP 下的文章

用Go写PHP小扩展

问:为什么是小扩展
答:因为我还没在生产环境上测试过~~囧~

环境

  • ubuntu 16.04
  • go1.6.3 linux/amd64
  • php-go
  • php7.0.9

php-go安装

参考说明文档

示例

示例模块php-go/example/hello.go中比较全的写出了大部分示例(虽然没多少注释~)

# 编译示例模块hello.so
cd cd $GOPATH/src/github.com/kitech/php-go
make
ls -lh php-go/hello.so

tips: make时可能会出现make: /usr/bin/php-config:命令未找到,手动修改下Makefile的第10行PHPCFG={yourPath}

将php-go/hello.so拷贝到你php的extension目录下,添加php.ini,php -m |grep hello能看到hello扩展没报错就ok了,重启nginx就可以在php里测试了

编写自己的小Demo

  • go
// php-go/example/jw.go
package main

import (
    "github.com/kitech/php-go/phpgo"
)

type Jw struct {
}

func NewJw() *Jw {
    return &Jw{}
}

func (j *Jw) Test(m int, n int) int {
    return m + n
}

func main() {
    panic("wtf")
}

func Jw_hello() string {
    return "Aaaa"
}

func init() {
    phpgo.InitExtension("jw", "0.1")
    phpgo.AddFunc("jw_hello", Jw_hello)
    phpgo.AddClass("Jw", NewJw)
}
  • 编译

    • 先修改php-go/Makefile,添加编译模块

      all:
      go build -v -buildmode=c-shared -o jw.so examples/jw.go
      clean:
      rm -f jw.so
- 编译
cd cd $GOPATH/src/github.com/kitech/php-go
make
ls -lh php-go/jw.so

sudo cp $GOPATH/src/github.com/kitech/php-go/jw.so  $PHP_PATH/lib/php/extensions/no-debug-non-zts-20151012
echo "jw.so" >> $PHP_PATH/etc/php.ini
sudo service nginx restart

- 写php测试模块

php-ext-result

PHP+Hadoop实现数据统计分析

记一次完全独立完成的统计分析系统的搭建过程,主要用到了PHP+Hadoop+Hive+Thrift+Mysql实现

流程图

安装


Hadoop安装: http://www.powerxing.com/install-hadoop/
Hadoop集群配置: http://www.powerxing.com/install-hadoop-cluster/
Hive安装: https://chu888chu888.gitbooks.io/hadoopstudy/content/Content/8/chapter0807.html

安装具体教程请看上面链接,本地测试只用了单机配置,集群配置(后面的flume用到)看上面的详细链接, 因为之前没有接触过java的相关,这里说下遇到的几个问题.

  • Hadoop和Hive的1.x和2.x版本要对应
  • JAVA/Hadoop相关的环境变量配置,习惯了PHP的童鞋在这块可能容易忽略
  • 启动Hadoop提示Starting namenodes on [],namenodes为空,是因为没有指定ip或端口,修改hadoop/core-site.xml如下
<configuration>
<property>
<name>dfs.namenode.rpc-address</name>
<value>127.0.0.0:9001</value>
</property>
</configuration>
  • 安装完成后输入jps可以查看到NameNode,DataNode等

上报和接收


  • swoole和workerman都有简单版本实现的数据监控,包括上报,接收,存储,展示, 主要使用udp上传(swoole版本已升级为tcp长连接),redis缓存,文件持久化,highcharts展示,可以作为思路参考
    swoole-statistics : https://github.com/smalleyes/statistics

workerman-statistics : https://github.com/walkor/workerman-statistics

  • 本例使用swoole提供的接口实现UDP传输,因为上报数据是一定程度可以容错,所以选择UDP效率优先
  • 接收数据临时存储在Redis中,每隔几分钟刷到文件中存储,文件名按模块和时间分割存储,字段|分割(后面与hive对应)

数据转存


创建Hive数据表

  • 根据文件数据格式编写Hive数据表, TERMINATED BY字段与前面文件字段分隔符想对应
  • 对表按日期分区PARTITIONED BY
CREATE TABLE login (
    time int comment '登陆时间', 
    type string comment '类型,email,username,qq等', 
    device string comment '登陆设备,pc,android,ios', 
    ip string comment '登陆ip', 
    uid int comment '用户id', 
    is_old int comment '是否老用户'
) 
PARTITIONED BY (
     `date` string COMMENT 'date'
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '|';
  • 定时(Crontab)创建hadoop分区
hive -e "use web_stat; alter table login add if not exists partition (date='${web_stat_day}')"

转存

  • Flume监听文件目录,将数据传输到能访问Hdfs集群的服务器上,这里传输到了224机器的7000端口
#agent3表示代理名称 login
agent3.sources=source1
agent3.sinks=sink1
agent3.channels=channel1

#配置source1
agent3.sources.source1.type=spooldir
agent3.sources.source1.spoolDir=/data/releases/stat/Data/10001/
agent3.sources.source1.channels=channel1
agent3.sources.source1.fileHeader = false

#配置sink1
agent3.sinks.sink1.type=avro
agent3.sinks.sink1.hostname=192.168.23.224
agent3.sinks.sink1.port=7000
agent3.sinks.sink1.channel=channel1


#配置channel1
agent3.channels.channel1.type=file
agent3.channels.channel1.checkpointDir=/data/flume_data/checkpoint_login
agent3.channels.channel1.dataDirs=/data/flume_data/channelData_login
  • 启动flume
# 加到supervisor守护进程
/home/flume/bin/flume-ng agent -n agent3 -c /home/flume/conf/ -f /home/flume/conf/statistics/login_flume.conf  -Dflume.root.logger=info,console
  • 224机器监听7000端口,将数据写到hdfs集群
#agent1表示代理名称
agent4.sources=source1
agent4.sinks=sink1
agent4.channels=channel1


#配置source1
agent4.sources.source1.type=avro
agent4.sources.source1.bind=192.168.23.224
agent4.sources.source1.port=7000
agent4.sources.source1.channels=channel1

#配置sink1
agent4.sinks.sink1.type=hdfs
agent4.sinks.sink1.hdfs.path=hdfs://hdfs/umr-ubvzlf/uhiveubnhq5/warehouse/web_stat.db/login/date\=%Y-%m-%d
agent4.sinks.sink1.hdfs.fileType=DataStream
agent4.sinks.sink1.hdfs.filePrefix=buffer_census_
agent4.sinks.sink1.hdfs.writeFormat=TEXT
agent4.sinks.sink1.hdfs.rollInterval=30
agent4.sinks.sink1.hdfs.inUsePrefix = .
agent4.sinks.sink1.hdfs.rollSize=536870912
agent4.sinks.sink1.hdfs.useLocalTimeStamp = true
agent4.sinks.sink1.hdfs.rollCount=0
agent4.sinks.sink1.channel=channel1


#配置channel1
agent4.channels.channel1.type=file
agent4.channels.channel1.checkpointDir=/data/flume_data/login_checkpoint
agent4.channels.channel1.dataDirs=/data/flume_data/login_channelData
  • 启动
# 加到supervisor守护进程
/usr/local/flume/bin/flume-ng agent -n agent4 -c /usr/local/flume/conf/ -f /usr/local/flume/conf/statistics/login_flume.conf -Dflume.root.logger=info,console

清洗数据


通过Thrift的PHP扩展包调用Hive,编写类SQL的HQL转换为MapReduce任务读取计算HDFS里的数据, 将结果存储在MySQL中
php-thrift-client下载地址: https://github.com/garamon/php-thrift-hive-client

define('THRIFT_HIVE' , ROOT .'/libs/thrift');
$GLOBALS['THRIFT_ROOT'] = THRIFT_HIVE . '/lib';
require_once $GLOBALS['THRIFT_ROOT'] . '/packages/hive_service/ThriftHive.php';
require_once $GLOBALS['THRIFT_ROOT'] . '/transport/TSocket.php';
require_once $GLOBALS['THRIFT_ROOT'] . '/protocol/TBinaryProtocol.php';
require_once THRIFT_HIVE . '/ThriftHiveClientEx.php';

$transport = new \TSocket('127.0.0.1', 10000);
$transport->setSendTimeout(600 * 1000);
$transport->setRecvTimeout(600 * 1000);
$this->client = new \ThriftHiveClientEx(new \TBinaryProtocol($transport));
$this->client->open();
$this->client->execute("show databases");
$result = $this->client->fetchAll();
var_dump($result);
$this->client->close();
select * from login limit 5;
// php处理
$count = 0;
foreach ($queryResult as $row) {
  $count ++;
}
  • 一次性转换为MapReduce,利用Hadoop的计算能力
select type,count(*) from login group by type;  // 这样就用到了
  • 建表使用了PARTITIONED BY分区断言后,查询就可以利用分区剪枝(input pruning)的特性,但是断言字段必须离where关键字最近才能被利用上
// 如前面的login表使用到了date分区断言,这里就得把date条件放在第一位
select count(*) from login where date='2016-08-23' and is_old=1;
  • Hive中不支持等值连表,如下
select * from dual a,dual b where a.key = b.key;

应写为:

select * from dual a join dual b on a.key = b.key;
  • Hive中不支持insert,而且逻辑上也不允许,应为hadoop是我们用来做大数据分析,而不应该作为业务细分数据

数据报表展示


这一步就简单了,读取MySQL数据,使用highcharts等工具做各种展示,也可以用crontab定时执行php脚本发送日报,周报等等

后续更新

最近看一些资料和别人沟通发现,清洗数据这一步完全不用php,可以专注于HQL实现清洗逻辑,将结果保存在hadoop中,再用Sqoop将hadoop数据和MySQL数据同步。即简化了流程,免去mysql手工插入,又做到了数据更实时,为二次清洗逻辑的连表HQL做了铺垫

PHP的DES加密和RSA签名(兼容java)

主要用于php对接java的接口
rsa签名用SHA1WithRSA算法

<?php
/**
 * DES加密/解密,RSA加密/验签
 * @author jiangwei<jw90098@gmail.com>
 * @version $Id: Crypt.class.php  $
 */

class Crypt
{

    // DES加密
    public function encrypt($str, $key)  {  
        $block = mcrypt_get_block_size('des', 'ecb');  
        $pad = $block - (strlen($str) % $block);  
        $str .= str_repeat(chr($pad), $pad);  
        $str = mcrypt_encrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);  
        return base64_encode($str);  
    }  

    // DES解密
    public function decrypt($str, $key) {  
        $str = base64_decode($str);  
        $str = mcrypt_decrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);  
        $block = mcrypt_get_block_size('des', 'ecb');  
        $pad = ord($str[($len = strlen($str)) - 1]);  
        return substr($str, 0, strlen($str) - $pad);  
    }  


    // RSA签名(SHA1WithRSA算法)
    public function sign($content, $rsaPrivateKeyPem) {
            $priKey = file_get_contents($rsaPrivateKeyPem);
            $res = openssl_get_privatekey($priKey);
            openssl_sign($content, $sign, $res);
            openssl_free_key($res);
            $sign = base64_encode($sign);
            return $sign;
     }

     // RSA验签
     public function verify($data, $sign, $publicRsaPath)  {
        //读取公钥文件
        $pubKey = file_get_contents($publicRsaPath);
        //转换为openssl格式密钥
        $res = openssl_get_publickey($pubKey);
        //调用openssl内置方法验签,返回bool值
        $result = (bool)openssl_verify($data, base64_decode($sign), $res);
        //释放资源
        openssl_free_key($res);
        //返回资源是否成功
        return $result;
    }
}

php7 废除了 mcrypt_encrypt, 改用 openssl_encrypt

class DES{
    public static function encrypt($str,$key){
        $str = self::pkcs5_pad($str, 8);
        if (strlen($str) % 8) {
            $str = str_pad($str,
                strlen($str) + 8 - strlen($str) % 8, "\0");
        }
        $sign = openssl_encrypt (
            $str,
            'DES-EDE3' ,
            $key,
            OPENSSL_RAW_DATA | OPENSSL_NO_PADDING ,
            ''
        );

        return strtoupper(bin2hex($sign));
    }

    private static function pkcs5_pad($text, $blocksize) {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }
}

对比示例 java php

模拟易名中国域名的点赞

易名中国的域名有点赞功能,点赞数高可以被推荐,但只能点一次,那么需求就来了

ename.jpg

方法一: ajax请求

与源代码的js类似,需要注意的是跨域问题, 因为请求的二级域名是www,所以也要在www主机页面的控制台(console)里跑,否则会返回非法请求

var praise = 0, num = 123456;   // num为你域名页面的id,在浏览器的url中可以直观看到
var _this = $('#praise');
var url = "http://www.ename.com/auction/praise"+'/' + num + '/' + praise;
// 这是它页面的源代码
$.ajax({
        url:url,
        type:'get',
        dataType:'json',
        success:function(data) {
            switch(data.flag) {
                case 1:
                    _this.find('.praise_amount').html("("+data.count+")");
                    var html = '<div class="dz_tip"><span class="outsideb"></span><span class="insideb"></span>点赞成功</div>';
                        _this.find('.praise_r').after(html);
                    $('.dz_tip').stop().fadeOut(2000, function() {
                        $(this).remove();
                    })
                    praise = data.praise;
                    break;
                case 2:
                default:
                    alert(data.msg);
            }
        }
});
  • 问题: 由于它服务端有对ip的判断,所以这代码只能用一次,而且不利于在linux服务器上跑脚本,所以替换方案来了

方法二: curl模拟

<?php

header('Content-Type: text/html; charset=utf-8');

// $num为你页面域名的id,可以在浏览器的url中直观看到
function run($num, $second = 30){

    $url = "http://www.ename.com/auction/praise/$num";

    $headers['CLIENT-IP'] = '202.103.229.46'; 
    $headers['X-FORWARDED-FOR'] = '202.103.229.46';
    $headers['X-Requested-With'] = 'XMLHttpRequest';    // 重要
    // X-Requested-With:XMLHttpRequest

    $headerArr = array(); 
    foreach( $headers as $n => $v ) { 
        $headerArr[] = $n .':' . $v;  
    }

    $ch = curl_init();
    curl_setopt ($ch, CURLOPT_URL, $url.'/0');
    curl_setopt ($ch, CURLOPT_HEADER, 1);
    curl_setopt ($ch, CURLOPT_HTTPHEADER , $headerArr );  //构造IP
    // curl_setopt ($ch, CURLOPT_REFERER, $url);   //构造来路

    $content = curl_exec($ch);
    curl_close($ch);
    return $content;
}

$res = run(123456);
  • 可以再对其优化下,加个循环把ip随机, 再加个sleep停顿下,再在header中中随机个UA模拟下更像
  • 返回结果如下就成功了
{"flag":1,"msg":"\u70b9\u8d5e\u6210\u529f","count":48,"praise":1}

再次提醒慎用,导致域名被封了概不负责

php的session超时问题

session通常是根据php.ini中的 session.gc_maxlifetimie 的来控制超时时间,gc回收机制是有概率的,也就是说超时过期后不一定会清掉session

gc回收概率是通过php.ini中的session.gc_probabilitysesssion.gc_divisor决定的;

回收几率 = probability / divisor

session.gc_probability默认值为1; session.gc_divisor默认值为1000;也就是说session默认是1/1000分之一的概率,每1000次才会触发一次回收机制,当访问量小,比如只有一个用户在线时,该用户session超时了也不一定会被清掉,这就不能满足我们程序的严谨性。

解决方案一

修改session.gc_probability的值为1000,这样概率达到100%,每次操作都会触发回收机制,保证无漏网之鱼

解决方案二

为了程序的移植方便,抛弃配置文件自动回收; 通过代码自己验证session时间,可能更为灵活

  • 用户登陆后在当前session里存入当前时间
// login.php
$_SESSION['user']['onlinetime'] = time();
  • 在程序的基类中靠前位置加入判断
// baseClass.php
$nowtime = time();
$outtime = 1800;        // 过期时间为30分钟
if($nowtime - $_SESSION['user']['onlinetime'] > $outtime){
        session_destroy();
}else{
        $_SESSION['user']['onlinetime'] = $nowtime;
}

// ... 后续你肯定有对session的判断,session为空跳登陆页面咯

效率问题

没有对效率实测,个人觉得第二种方案效率略高,而且在相同环境下若存在多个站,能不影响别的站

一些常用php的header头

<?php 
header('HTTP/1.1 200 OK'); // ok 正常访问
header('HTTP/1.1 404 Not Found'); //通知浏览器 页面不存在
header('HTTP/1.1 301 Moved Permanently'); //设置地址被永久的重定向 301
header('Location: http://www.ithhc.cn/'); //跳转到一个新的地址
header('Refresh: 10; url=http://www.ithhc.cn/'); //延迟转向 也就是隔几秒跳转
header('X-Powered-By: PHP/6.0.0'); //修改 X-Powered-By信息
header('Content-language: en'); //文档语言
header('Content-Length: 1234'); //设置内容长度
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $time).' GMT'); //告诉浏览器最后一次修改时间
header('HTTP/1.1 304 Not Modified'); //告诉浏览器文档内容没有发生改变

###内容类型### 
header('Content-Type: text/html; charset=utf-8'); //网页编码 
header('Content-Type: text/plain'); //纯文本格式 
header('Content-Type: image/jpeg'); //JPG、JPEG 
header('Content-Type: application/zip'); // ZIP文件 
header('Content-Type: application/pdf'); // PDF文件 
header('Content-Type: audio/mpeg'); // 音频文件 
header('Content-type: text/css'); //css文件
header('Content-type: text/javascript'); //js文件
header('Content-type: application/json'); //json
header('Content-type: application/pdf'); //pdf 
header('Content-type: text/xml'); //xml
header('Content-Type: application/x-shockw**e-flash'); //Flash动画 

###### 

###声明一个下载的文件###
header('Content-Type: application/octet-stream'); 
header('Content-Disposition: attachment; filename="ITblog.zip"'); 
header('Content-Transfer-Encoding: binary'); 
readfile('test.zip');
######

###对当前文档禁用缓存###
header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate'); 
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); 
######

###显示一个需要验证的登陆对话框### 
header('HTTP/1.1 401 Unauthorized'); 
header('WWW-Authenticate: Basic realm="Top Secret"'); 
######


###声明一个需要下载的xls文件###
header('Content-Disposition: attachment; filename=ithhc.xlsx');
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Length: '.filesize('./test.xls')); 
header('Content-Transfer-Encoding: binary'); 
header('Cache-Control: must-revalidate'); 
header('Pragma: public'); 
readfile('./test.xls'); 
######


?>

life by php

<?php

$days = rand(1, 9999999); //生命里有各种不测
$problems = rand(0, 9999999); //人生充满了各种问题

while ($days){ //只要活着
    $days--; //日子就要一天天走着
    (rand(0, 9999999)%2) ? $problems++ : $problems--; //或许问题总会解决
}

die(); //但总有一天你会离开
echo '时间不会给你足够的时间领悟';

PHP的mail函数

// 发送邮件
function send($mails){
    $to = 'a@a.com, b@b.com';
    $title = '检查结果';
    $content = "以下订单号有错误" . "\r\n";
    $content .= "id:1,2,3";
    $headers = "MIME-Version: 1.0" . "\r\n";
    $headers .= "Content-type:text/html;charset=utf-8" . "\r\n";
    $headers .= "From: system@system.com";    // 自定义
    $r = mail($to, $title, $content, $headers);

    var_dump($r);
}

注意的问题


  • 标题做编码处理(不同客户端的编码不同,可能会乱码)
$subject = iconv('','GB2312','亲爱的'.$s_user.',请取回您的密码!');
  • Linux环境
    ·linux·下注意·/usr/sbin/sendmail· 或者·/usr/bin/sendmail·是否存在

若不存在可能是没安装

sudo apt-get install sendmail

然后就可以直接用 PHP 内置的mail()函数了

  • php.ini 配置
[mail function]
; For Win32 only.
SMTP = smtp.126.com
#smtp_port = 25

; For Win32 only.
sendmail_from = jiangwei8909@126.com

; For Unix only. You may supply arguments as well (default: "sendmail -t -i ").
;sendmail_path =

前三项对windows环境生效 sendmail_pathunix生效

PHP执行时间类

其实也就是通过microtime函数在前后个执行一下,计算时间差获取代码段的执行时间,测试效率

$runtime->stop();
echo $_b;
echo "页面执行时间: ".$runtime->spent()." 毫秒";

class runtime
{
    var $StartTime = 0;
    var $StopTime = 0;
    function get_microtime()
    {
        list($usec, $sec) = explode(' ', microtime());
        return ((float)$usec + (float)$sec);
    }

    function start()
    {
        $this->StartTime = $this->get_microtime();
    }

    function stop()
    {
        $this->StopTime = $this->get_microtime();
    }

    function spent()
    {
        return round(($this->StopTime - $this->StartTime) * 1000, 1);
    }
}

原文出自开源中国

utf8转unicode的总结

工作上遇到转unicode的一些总结和记录

全部转


// 全部转
function unicode_encode2($name)
{
    $name = iconv('UTF-8', 'UCS-2', $name);
    $len = strlen($name);
    $str = '';
    for ($i = 0; $i < $len - 1; $i = $i + 2)
    {
        $c = $name[$i];
        $c2 = $name[$i + 1];
        if (ord($c) > 0)
        { //两个字节的文字
            $str .= '\u'.str_pad(base_convert(ord($c2), 10, 16), 2, 0, STR_PAD_LEFT).base_convert(ord($c), 10, 16);
        }
        else
        {
            $str .= $c2;
        }
    }
    return $str;
}

只转中文,英文及逗号不转


// 只转中文,英文及逗号不转
// 通过字节判断,中文两个字节,只转两个字节的字符
function unicode_encode($name)
{
    $name = iconv('UTF-8', 'UCS-2', $name);
    $len = strlen($name);
    $str = '';
    for ($i = 0; $i < $len - 1; $i = $i + 2)
    {
        $c = $name[$i];
        $c2 = $name[$i + 1];
        if (ord($c2) > 0)
        { //两个字节的文字
            $str .= '\u'.base_convert(ord($c2), 10, 16).str_pad(base_convert(ord($c), 10, 16), 2, 0, STR_PAD_LEFT);
        }
        else
        {
            $str .= $c;
        }
    }
    return $str;
}

由于特殊字符也占一个字节,同时也需要转


// 由于特殊字符也占一个字节,同时也需要转
function unicode_encode2($name)
{
    $name = iconv('UTF-8', 'UCS-2', $name);
    $len = strlen($name);
    $str = '';
    for ($i = 0; $i < $len - 1; $i = $i + 2)
    {
        $c = $name[$i];
        $c2 = $name[$i + 1];
        if (ord($c) > 0)
        { //两个字节的文字
            $str .= '\u'.str_pad(base_convert(ord($c2), 10, 16), 2, 0, STR_PAD_LEFT).base_convert(ord($c), 10, 16);
        }
        else
        {
            $str .= $c2;
        }
    }
    return $str;
}
// '·'

解码


// 解码
//将UNICODE编码后的内容进行解码
function unicode_decode($name)
{
    //转换编码,将Unicode编码转换成可以浏览的utf-8编码
    $pattern = '/([\w]+)|(\\\u([\w]{4}))/i';
    preg_match_all($pattern, $name, $matches);
    if (!empty($matches))
    {
        $name = '';
        for ($j = 0; $j < count($matches[0]); $j++)
        {
            $str = $matches[0][$j];
            if (strpos($str, '\\u') === 0)
            {
                $code = base_convert(substr($str, 2, 2), 16, 10);
                $code2 = base_convert(substr($str, 4), 16, 10);
                $c = chr($code).chr($code2);
                $c = iconv('UCS-2', 'UTF-8', $c);
                $name .= $c;
            }
            else
            {
                $name .= $str;
            }
        }
    }
    return $name;
}

XAMPP安装memcache扩展的总结

先说点破事


  • 首先XAMPP是个不错的lampp集成包,安装和卸载方便,只需要解压和删除安装目录即可,快速便捷
  • 之前用的xampp包怎么也装不上扩展(xdebug),网上有朋友说因为没有装develop开发套件,我弄了个develop套件解压进去还是不行,苦恼了好一阵,忍忍就没再搞了
  • 上周又一次用到了memecache,这次不搞不行了,也决心把本地环境搞好,遂有了下面的曲折历程,记录之,由于过程波折可能会有点絮叨,见谅
  • 本机环境ubuntu12.04

XAMPP安装


  • 官网 https://www.apachefriends.org/zh_cn/index.html
  • 第一次安装的是1.7版本的tar包,不带develop开发套件,这一次是重新下载最新版的xampp-linux-5.5.19-0-installer.run,备注:.run文件安装需要先把文件权限调整为允许以程序执行文件,直接给777也行,具体可自行百度

开始memcache安装


参照 http://www.ccvita.com/257.html做的改动,感谢原博主

  • 安装memcache
sudo apt-get install memcache
  • 安装memcache的php扩展,通过编译方式安装,下载memcache的php扩展包,下载链接

    • 网上也有说通过sudo apt-get install php5-memcache安装,这里可能会和ubuntu自带的环境冲突,关联不到xampp上
  • 进入memcache的目录
tar vxzf memcache-2.2.1.tgz
cd memcache-2.2.1
/opt/lampp/bin/phpize
./configure --enable-memcache --with-php-config=/opt/lampp/bin/php-config --with-zlib-dir
make
make install
  • 重启apache,启动memcache
memcached -d -m 200 -u root -p 11211
  • 查看 /opt/lampp/bin/php -m 如果有memcache基本就成了,php如果连不上那就是启动端口问题

遇到的问题:


  • phpize根据你自己xampp安装的实际路径

configure报错


  • 开始按原文./configure --enable-memcache --with-php-config=/usr/local/php/bin/php-config --with-zlib-dir
  • 报错configure: error: Cannot find php-config. Please use --with-php-config=PATH
  • 我傻傻的跑了下./configure --enable-memcache --with-php-config=PATH
  • 再报错configure: error: Cannot find php-config. Please use --with-php-config=PATH
  • 我意识到PATH,参照原文的/usr/local/php/bin/php-config,知道了问题出在php-config
  • 直接去xampp的安装目录中搜索对应的php-config,然后获取路径换上

make install报错


  • 报错信息
cp: cannot create regular file `/opt/lampp/lib/php/extensions/no-debug-non-zts-20121212/    #INST@12000#': Permission denied
make: *** [install-modules] 错误 1
  • 干掉之前调试时的所有memcache进程
  • 干掉/opt/lampp/bin/php/extensions/no-debug-XXX/中之前复制过的memcache.so文件
  • 重点:把刚刚那个编译目录中的memcache.so拿过来放进去
sudo cp ~/memcache-2.2.7/modules/memcache.so  /opt/lampp/lib/php/extensions/no-debug-non-zts-20121212/
  • 在php.ini中添加memcache.so
  • 貌似无所谓修改php.iniextension_dir,这个目录是根据自己的真实目录
extension_dir = "/opt/lampp/lib/php/extensions/no-debug-non-zts-20121212/"
  • 再重启apache,启动memcache,启动命令 memcached -d -m 200 -u root -p 11211
  • 再查看 /opt/lampp/bin/php -m 如果有memcache基本就成了,php如果连不上那就是启动端口问题