利用PHP调用BizMail OpenApi(腾讯企业邮箱)进行接口开发

以下引自《腾讯企业邮箱OpenApi协议v1.0

腾讯企业邮箱开放协议,包括面向第三方合作应用和面向企业邮用户两类。其中,面向
企业邮用户的开放协议, 将提供给企业邮用户丰富的应用接口, 用户可以根据这些接口定制
自己统一的企业解决方案。
通过协议接口,企业用户可以实现:
1) 单点登录
可以从公司 OA 系统、网站一键进入企业邮箱,免去登录过程。
2) 新邮件提醒
新邮件将即时在 OA 等办公系统提醒你。
3) 数据同步
数据同步可以帮助你同步部门成员信息, 你还可以创建、 删除、 修改帐号, 同步部门信息等。

我一直想用PHP来实现这些功能的调用,把腾讯官方提供的这份简单的文档研究了下,终于弄出来了。做个笔记,如下:
在使用OpenApi接口之前,要先准备好开通腾讯企业邮箱的管理员ID,接口Key(这个可以去企业邮箱管理后台获取)和需要调用信息的邮箱名,然后就可以进行相关调用了,PHP代码如下:

 

// 首先获取AccessToken
 
// 企业邮箱的管理员ID
$cTMailID = 'mymailid';
// 在管理后台获取的接口Key
$cTMailSecret = 'a1234567890b1234567890c123456789';
// 需要调用信息的邮箱名
$cTMailAlias = 'mailalias';
// 获取Token的地址
$cTMailGetAccessTokenURL = 'https://exmail.qq.com/cgi-bin/token';
$cTMailContentData = 'grant_type=client_credentials&client_id=' . $cTMailID . '&client_secret=' . $cTMailSecret;
 
// 获取access_token
// 不要问我为什么这么写,早就忘记了!-_-
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailGetAccessTokenURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
$cTMailAccessToken = $json_obj['access_token'];
 
// 打印获取的AccessToken
echo $cTMailAccessToken . '
';
 
 
 
// 获取auth_key
$cTMailGetAuthKeyURL = 'http://openapi.exmail.qq.com:12211/openapi/mail/authkey';
$cTMailContentData = 'access_token=' . $cTMailAccessToken . '&alias=' . $cTMailAlias;
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailGetAuthKeyURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
$cTMailAuthKey = $json_obj['auth_key'];
 
 
// 一键登录
$cURL = 'https://exmail.qq.com/cgi-bin/login?fun=bizopenssologin&method=bizauth&agent=' . $cTMailID . '&user=' . $cTMailAlias . '&ticket=' . $cTMailAuthKey;
echo '一键登录';
 
 
// 获取用户信息
$cTMailPostURL = 'http://openapi.exmail.qq.com:12211/openapi/user/get';
$cTMailContentData = 'access_token=' . $cTMailAccessToken . '&alias=' . $cTMailAlias;
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailPostURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT, 75);
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
print_r($json_obj);
echo '
';
echo 'Email地址:' . $json_obj['Alias'] . '
';
echo '姓名:' . $json_obj['Name'] . '
';
echo '性别:' . $json_obj['Gender'] . '
';
echo '职位:' . $json_obj['Position'] . '
';
echo '电话:' . $json_obj['Tel'] . '
';
echo '手机号码:' . $json_obj['Mobile'] . '
';
echo '员工编号:' . $json_obj['ExtId'] . '
';
 
 
 
// 获取用户未读邮件数量
$cTMailPostURL = 'http://openapi.exmail.qq.com:12211/openapi/mail/newcount';
$cTMailContentData = 'access_token=' . $cTMailAccessToken . '&alias=' . $cTMailAlias;
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailPostURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT, 75);
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
// 打印获取的信息
print_r($json_obj);
 
 
// 客户端维持长连接
$cTMailPostURL = 'http://openapi.exmail.qq.com:12211/openapi/listen';
$cTMailContentData = 'access_token=' . $cTMailAccessToken . '&alias=' . $cTMailAlias;
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailPostURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT, 75);
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
print_r($json_obj);
 
 
 
// 获取子部门列表
$cTMailPostURL = 'http://openapi.exmail.qq.com:12211/openapi/party/list';
$cTMailContentData = 'access_token=' . $cTMailAccessToken . '&partypath=';
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailPostURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
echo '
获取的子部门列表:';
print_r($json_obj);
 
 
 
// 获取部门下成员列表
$cTMailPostURL = 'http://openapi.exmail.qq.com:12211/openapi/partyuser/list';
$cTMailContentData = 'access_token=' . $cTMailAccessToken . '&partypath=XX公司/财务部';
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailPostURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
echo '
获取的子部门成员列表:';
print_r($json_obj);

原文参考:http://www.barhe.org/archives/561
官方手册:http://cdn.markdream.com/ref/mat/%E8%85%BE%E8%AE%AF%E4%BC%81%E4%B8%9A%E9%82%AE%E7%AE%B1OpenApi%E5%8D%8F%E8%AE%AEv1.0.pdf

nginx使用XSendfile模块让php更快下载文件实战

引言

最近有点闲暇时间了,看了@风雪之隅的一篇的文章《让PHP更快的提供文件下载》后,利用实际项目中的业务场景觉得有必须要去优化附件下载功能了。鸟哥的文章里面主要介绍的基于apache来做XSendfile讲解的,我有点强迫症,我对我的生产环境《顶岗实习管理系统》进行升级改造,我的webserver是nginx,所以基于nginx官方网站的XSendfile说明特做此改造笔记。

我的webroot目录在/var/vhost/demo,demo目录中存在一个uploadfiles文件夹,这个文件夹是存放用户上传的文件。比如一个用户上传一个hello.docx文件,那么它对应的物理路径应该是/var/vhost/demo/xsendfile/uploadfiles/hello.docx,如果我们以直接路径“http://demo.markdream.com/xsendfile/uploadfiles/hello.docx”来访问的话,那么我想对这个文件进行下载统计或者隐藏实体路径的话就只能使用php readfile()方法来把文件装载到内存中,在转发给客户端,如果文件超大的话,你的服务器估计很悬。所以我们要改进php下载方式,就是引言所阐述的利用nginx的XSendfile方式来传送文件给客户端,nginx默认已经包含了senfile模块了,你如果安装完nginx你会在nginx.conf文件会看到“sendfile on;”,OK说明你可以直接使用了不像apache那么复杂还要重新加载组件编译等等。好了我贴下我的配置文件和代码。

我的nginx虚拟主机配置文件

server {
        listen 80;
        server_name demo.markdream.com;

        root /var/vhost/demo;
        index  index.php;

		# 这个是定义读取你的文件的目录的url开头  直接访问是不可以的 只能通过
        location /protected {
                internal;
                alias   /var/vhost/demo/uploadfiles;
        }

        location ~ \.php$ {
           fastcgi_pass   127.0.0.1:9000;
           fastcgi_index  index.php;
           fastcgi_param  SCRIPT_FILENAME  $document_root/$fastcgi_script_name;
           include        fastcgi_params;
        }
}

一个简单的php下载文件脚本,downloads.php

<?php

//eg: http://demo.markdream.com/xsendfile/downloads.php?filename=hello.docx

// 获取文件名
$filename = $_GET["filename"];

// 你可以在这里写下你的查询数据库等你所想的功能 ……

header ( "Content-type: application/octet-stream" );
// 处理中文文件名
$ua = $_SERVER ["HTTP_USER_AGENT"];
if (preg_match ( "/MSIE/", $ua )) {
 $encoded_filename = rawurlencode ( $filename );
 header ( 'Content-Disposition: attachment; filename="' . $encoded_filename . '"' );
} else if (preg_match ( "/Firefox/", $ua )) {
 header ( "Content-Disposition: attachment; filename*=\"utf8''" . $filename . '"' );
} else {
 header ( 'Content-Disposition: attachment; filename="' . $filename . '"' );
}

// 就这么简单一句话搞定 注意“protected”是和nginx配置文件的 protected要一致
header("X-Accel-Redirect: /protected/" . $filename);

?>

愉悦的demo

http://demo.markdream.com/xsendfile/downloads.php?filename=hello.docx

参考

  1. http://wiki.nginx.org/XSendfile
  2. http://www.laruence.com/2012/05/02/2613.html
  3. http://kovyrin.net/2006/11/01/nginx-x-accel-redirect-php-rails/

让php获取图片的exif信息

生产环境中,我们有业务上的需求,比如搞什么摄影网站,或者相册图集功能。用户上传的图片我们不仅要满足用户上传图片的保存,还应该将一些专业的照片参数进行保存起来,这样在一些专业的图片网站上(比较摄影爱好者论坛)应该要分享这些相机型号、感光度、光圈、快门等等信息。

在默认的php版本中是没有对php_exif扩展的安装所以还需要我们针对服务器进行安装php-exif安装支持。
具体的检查你可以使用phpinfo()查看下是否存在exif组件项目,如果存在证明你的服务器已经安装了exif扩展,如果没有则安装之。

安装php_exif组件

要安装php_exif组件,首先我们还是要打开php源码安装文件夹中的ext/exif,命令如下(具体路径以你服务器实际地址为准)

cd /usr/local/src/php-5.4.8/ext/exif

/opt/php/bin/phpize

./configure --with-php-config=/opt/php/bin/php-config
 
make && make install

安装完毕之后,你需要将php_exif扩展添加到php.ini文件中

vi /opt/php/lib/php.ini

#找到extension列表中  添加一行  extension=exif.so

然后重启下php-fpm,再去phpinfo函数中查看下exif组件是否存在了。

使用exif获取照片信息

上个步骤就是在安装exif,下面我们来使用exif来获取照片信息,网络上面有一个很不错的类,我顺手把它们摘抄到这里了,已经做测试优化,直接使用是没有问题的。

<?php

error_reporting(0);

/**
* 获取图象信息的函数
*
* 一个全面获取图象信息的函数
*
* @access public
* @param string $img 图片路径
* @return array
*/
function GetImageInfoVal($ImageInfo,$val_arr) {
 $InfoVal = "未知";
 foreach($val_arr as $name=>$val) {
 if ($name==$ImageInfo) {
 $InfoVal = &$val;
 break;
 }
 }
 return $InfoVal;
}
function GetImageInfo($img) {
 $imgtype = array("", "GIF", "JPG", "PNG", "SWF", "PSD", "BMP", "TIFF(intel byte order)", "TIFF(motorola byte order)", "JPC", "JP2", "JPX", "JB2", "SWC", "IFF", "WBMP", "XBM");
 $Orientation = array("", "top left side", "top right side", "bottom right side", "bottom left side", "left side top", "right side top", "right side bottom", "left side bottom");
 $ResolutionUnit = array("", "", "英寸", "厘米");
 $YCbCrPositioning = array("", "the center of pixel array", "the datum point");
 $ExposureProgram = array("未定义", "手动", "标准程序", "光圈先决", "快门先决", "景深先决", "运动模式", "肖像模式", "风景模式");
 $MeteringMode_arr = array(
 "0" => "未知",
 "1" => "平均",
 "2" => "中央重点平均测光",
 "3" => "点测",
 "4" => "分区",
 "5" => "评估",
 "6" => "局部",
 "255" => "其他"
 );
 $Lightsource_arr = array(
 "0" => "未知",
 "1" => "日光",
 "2" => "荧光灯",
 "3" => "钨丝灯",
 "10" => "闪光灯",
 "17" => "标准灯光A",
 "18" => "标准灯光B",
 "19" => "标准灯光C",
 "20" => "D55",
 "21" => "D65",
 "22" => "D75",
 "255" => "其他"
 );
 $Flash_arr = array(
 "0" => "flash did not fire",
 "1" => "flash fired",
 "5" => "flash fired but strobe return light not detected",
 "7" => "flash fired and strobe return light detected",
 );
 
 $exif = exif_read_data ($img,"IFD0");
 if ($exif===false) {
 $new_img_info = array ("文件信息" => "没有图片EXIF信息");
 }else{
 $exif = exif_read_data ($img,0,true);
 $new_img_info = array (
 "文件信息" => "-----------------------------",
 "文件名" => $exif['FILE']['FileName'],
 "文件类型" => $imgtype[$exif['FILE']['FileType']],
 "文件格式" => $exif['FILE']['MimeType'],
 "文件大小" => $exif['FILE']['FileSize'],
 "时间戳" => date("Y-m-d H:i:s",$exif['FILE']['FileDateTime']),
 "图像信息" => "-----------------------------",
 "图片说明" => $exif['IFD0']['ImageDescription'],
 "制造商" => $exif['IFD0']['Make'],
 "型号" => $exif['IFD0']['Model'],
 "方向" => $Orientation[$exif['IFD0']['Orientation']],
 "水平分辨率" => $exif['IFD0']['XResolution'].$ResolutionUnit[$exif['IFD0']['ResolutionUnit']],
 "垂直分辨率" => $exif['IFD0']['YResolution'].$ResolutionUnit[$exif['IFD0']['ResolutionUnit']],
 "创建软件" => $exif['IFD0']['Software'],
 "修改时间" => $exif['IFD0']['DateTime'],
 "作者" => $exif['IFD0']['Artist'],
 "YCbCr位置控制" => $YCbCrPositioning[$exif['IFD0']['YCbCrPositioning']],
 "版权" => $exif['IFD0']['Copyright'],
 "摄影版权" => $exif['COMPUTED']['Copyright.Photographer'],
 "编辑版权" => $exif['COMPUTED']['Copyright.Editor'],
 "拍摄信息" => "-----------------------------",
 "Exif版本" => $exif['EXIF']['ExifVersion'],
 "FlashPix版本" => "Ver. ".number_format($exif['EXIF']['FlashPixVersion']/100,2),
 "拍摄时间" => $exif['EXIF']['DateTimeOriginal'],
 "数字化时间" => $exif['EXIF']['DateTimeDigitized'],
 "拍摄分辨率高" => $exif['COMPUTED']['Height'],
 "拍摄分辨率宽" => $exif['COMPUTED']['Width'],
 /*
 The actual aperture value of lens when the image was taken.
 Unit is APEX.
 To convert this value to ordinary F-number(F-stop),
 calculate this value's power of root 2 (=1.4142).
 For example, if the ApertureValue is '5', F-number is pow(1.41425,5) = F5.6.
 */
 "光圈" => $exif['EXIF']['ApertureValue'],
 "快门速度" => $exif['EXIF']['ShutterSpeedValue'],
 "快门光圈" => $exif['COMPUTED']['ApertureFNumber'],
 "最大光圈值" => "F".$exif['EXIF']['MaxApertureValue'],
 "曝光时间" => $exif['EXIF']['ExposureTime'],
 "F-Number" => $exif['EXIF']['FNumber'],
 "测光模式" => GetImageInfoVal($exif['EXIF']['MeteringMode'],$MeteringMode_arr),
 "光源" => GetImageInfoVal($exif['EXIF']['LightSource'], $Lightsource_arr),
 "闪光灯" => GetImageInfoVal($exif['EXIF']['Flash'], $Flash_arr),
 "曝光模式" => ($exif['EXIF']['ExposureMode']==1?"手动":"自动"),
 "白平衡" => ($exif['EXIF']['WhiteBalance']==1?"手动":"自动"),
 "曝光程序" => $ExposureProgram[$exif['EXIF']['ExposureProgram']],
 /*
 Brightness of taken subject, unit is APEX. To calculate Exposure(Ev) from BrigtnessValue(Bv), you must add SensitivityValue(Sv).
 Ev=Bv+Sv Sv=log((ISOSpeedRating/3.125),2)
 ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32. 
 */
 "曝光补偿" => $exif['EXIF']['ExposureBiasValue']."EV",
 "ISO感光度" => $exif['EXIF']['ISOSpeedRatings'],
 "分量配置" => (bin2hex($exif['EXIF']['ComponentsConfiguration'])=="01020300"?"YCbCr":"RGB"),//'0x04,0x05,0x06,0x00'="RGB" '0x01,0x02,0x03,0x00'="YCbCr"
 "图像压缩率" => $exif['EXIF']['CompressedBitsPerPixel']."Bits/Pixel",
 "对焦距离" => $exif['COMPUTED']['FocusDistance']."m",
 "焦距" => $exif['EXIF']['FocalLength']."mm",
 "等价35mm焦距" => $exif['EXIF']['FocalLengthIn35mmFilm']."mm",
 /*
 Stores user comment. This tag allows to use two-byte character code or unicode. First 8 bytes describe the character code. 'JIS' is a Japanese character code (known as Kanji).
 '0x41,0x53,0x43,0x49,0x49,0x00,0x00,0x00':ASCII
 '0x4a,0x49,0x53,0x00,0x00,0x00,0x00,0x00':JIS
 '0x55,0x4e,0x49,0x43,0x4f,0x44,0x45,0x00':Unicode
 '0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00':Undefined
 */
 "用户注释编码" => $exif['COMPUTED']['UserCommentEncoding'],
 "用户注释" => $exif['COMPUTED']['UserComment'],
 "色彩空间" => ($exif['EXIF']['ColorSpace']==1?"sRGB":"Uncalibrated"),
 "Exif图像宽度" => $exif['EXIF']['ExifImageLength'],
 "Exif图像高度" => $exif['EXIF']['ExifImageWidth'],
 "文件来源" => (bin2hex($exif['EXIF']['FileSource'])==0x03?"digital still camera":"unknown"),
 "场景类型" => (bin2hex($exif['EXIF']['SceneType'])==0x01?"A directly photographed image":"unknown"),
 "缩略图文件格式" => $exif['COMPUTED']['Thumbnail.FileType'],
 "缩略图Mime格式" => $exif['COMPUTED']['Thumbnail.MimeType']
 );
 }
 return $new_img_info;
}
 
$innerhtml = "";
$exif = GetImageInfo($_GET['img']);

$innerhtml .= "<table>";
 
foreach($exif as $name=>$val) {
 $innerhtml .= "<tr><td>{$name}</td><td>{$val}</td></tr>";
}
 
$innerhtml .= "<tr><td colspan=\"2\">";

$innerhtml .= "</td></tr></table>";
?>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>markdream.com</title>
<style>body{font-size:12px;}table{margin: 30px auto;}</style>
</head>
<body>

<?php echo $innerhtml;?>
</body>
</html>

 

 演示demo

http://demo.markdream.com/exif/x.php?img=zz.JPG

linux升级openssl和php_openssl模块

一、OpenSSL源码升级

2014年4月8日,XP宣布正式停止服务的日子,也是OpenSSL爆出大漏洞的日子。

OpenSSL主要是负责在一些敏感的数据提交上面被广泛使用,不乏大家经常访问的一些网站:支付宝、微信、淘宝、网银、社交、门户等知名网站。

官方上面推荐大家将OpenSSL升级到OpenSSL 1.0.1g

这不火急火燎的加入的升级大军,先查看下自己机器上的OpenSSL版本。

openssl version
#OpenSSL 1.0.0-fips 29 Mar 2010

很明显不是官方所说的版本,必须要升级好吧,我们以源码的形式。先去下载相对应的OpenSSL版本。

cd /usr/local/src/
wget http://www.openssl.org/source/openssl-1.0.1g.tar.gz
tar -zxvf openssl-1.0.1g.tar.gz
cd  openssl-1.0.1g
./config shared zlib
make && make install

#修改历史的OpenSSL文件设置备份
mv /usr/bin/openssl /usr/bin/openssl.old
mv /usr/include/openssl /usr/include/openssl.old

#设置软连接使其使用新的OpenSSL版本 刚刚安装的OpenSSL默认安装在/usr/local/ssl
ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl
ln -s /usr/local/ssl/include/openssl /usr/include/openssl

#更新动态链接库数据
echo "/usr/local/ssl/lib" >> /etc/ld.so.conf
ldconfig -v

我们再来看看OpenSSL版本信息.

openssl version

#OpenSSL 1.0.1g 7 Apr 2014

如果是1.0.1g,说明你安装正确了。

二、php_openssl组件版本更新

1、与php一同设置的编译参数情况

如果你和我一样很久很久以前安装了php的版本,而且又是在编译php版本的时候直接使用“–with-openssl”的话,似乎也没有添加什么路径的话,那么你现在可能需要重新再编译你的php版本了,首先我们要获取到当时编译php的参数,注意你的php源码版本要一致哦,当然你可以重新升级你的php版本也无妨(如果你选择升级php版本可能会导致你安装的一些组件无法使用,或者在启动php-fpm时候会有错误提示,我还是不太建议大家升级php版本,最好是找当前版本一直的源码进行重新编译)。

#查看php版本
/usr/local/php/bin/php -v

#获取php编译时的参数
/usr/local/php/bin/php -i | grep Command

#./configure'  '--prefix=/usr/local/php' '--with-mysql' '--with-mysqli' '--with-iconv-dir' '--with-zlib' '--with-libxml-dir' '--enable-xml' '--with-curl' '--enable-fpm' '--enable-mbstring' '--with-gd' '--with-openssl' '--with-mhash' '--enable-sockets' '--with-xmlrpc' '--enable-zip' '--enable-soap' '--disable-fileinfo'

注意把这些“’”都去掉,因为我们采用了源码升级OpenSSL的方式,所以新的OpenSSL安装路径在上面提到了

cd /usr/local/src/php-5.5.6

./configure  --prefix=/usr/local/php --with-mysql --with-mysqli --with-iconv-dir --with-zlib --with-libxml-dir --enable-xml --with-curl --enable-fpm --enable-mbstring --with-gd --with-openssl=/usr/local/ssl/ --with-mhash --enable-sockets --with-xmlrpc --enable-zip --enable-soap --disable-fileinfo

make && make install

你采用的是和之前源码安装一样的版本库无非就是重新编译一次需要点时间,其他都不需要修改就可以安装对openssl的升级了。我们重启下php-fpm服务。

service php-fpm restart

然后我们去phpinfo()函数输出里面去找找openssl的版本是否是OpenSSL 1.0.1g,是的话就证明一切操作都顺利了。
到这里或许你可能会说我为什么不用phpize了,这不是有点累死人的节奏,我是尝试过了,一开始就对php_openssl进行单独模块编译,编译好了之后,重新去启动php-fpm告诉我下面这个提示。

PHP Warning: Module 'openssl' already loaded in Unknown on line 0 ……

也就是说OpenSSL已经被加载了请不要重复加载,可是我将php.ini文件仔细查看也没有重复添加openssl组件,我的猜想应该是在php编译的时候配置了,所以你使用phpize添加进去的模块当然是重复的模块了。

2、未启用php_openssl模块

貌似这种情况有点差强人意的感觉,因为你从来没有使用过openssl,何必要升级呢?除非你有新的业务需要。咳咳,扯淡了。
如果你没有上面第一种情况的话,那么这种方式应该是你的最佳方式了。

cd /usr/local/src/php-5.5.6/ext/openssl

/usr/local/php/bin/phpize

./configure --with-openssl=/usr/local/ssl/ --with-php-config=/usr/local/php/bin/php-config

make && make install

最后就在/usr/local/php/lib/php.ini文件中添加一行

extension=openssl.so

重启php-fpm,去phpinfo()找找openssl的版本看看。

openssl_php_update

ubuntu下安装php开发环境笔记

本文主要介绍如何在ubuntu环境下安装一些常用的应用软件和开发软件,算给自己做个安装笔记吧。
基础环境介绍

OS:   ubuntu 12.04 64bit
CPU:  Intel(R) Core(TM) i3-2120 CPU @ 3.30GHz
Mem:  4G
Disk: 50G

本文所述安装软件清单

好了,上面阐述了本次要安装的软件,下面开始进入正题。

安装搜狗拼音输入法

一般的我们对于新的系统首要的应该是输入法了,没错,虽然默认的ubuntu在安装的过程中有附加的拼音输入法,但是我个人比较偏爱搜狗拼音输入法,这不第一个安装是就是输入法了。
由于搜狗输入法是基于fcitx框架开发的,所以我们在安装搜狗拼音输入法,如有必要请先卸载ibus。

sudo apt-get remove ibus

然后,添加fcitx源,更新资源

sudo add-apt-repository ppa:fcitx-team/nightly

sudo apt-get update

再进行安装搜狗拼音输入法。

sudo apt-get install fcitx fcitx-config-gtk fcitx-module-cloudpinyin fcitx-sogoupinyin im-switch

sudo im-switch -s fcitx -z default

#其实还有很多拼音输入法可选的 fcitx-sunpinyin fcitx-googlepinyin 
#fcitx-module-cloudpinyin 模块是对所有的拼音引擎添加了云支持,在第二个候选词的位置提供云结果

安装完成后,注销当前用户(推荐)或重启一下即可看到搜狗拼音输入法了。

安装谷歌浏览器(chrome)

一般来说ubuntu下面自带了Firefox浏览器的,不过我是一名忠实的chrome迷,我的tag和sync都在chrome上呢,当然还有我习惯的goagent(下步再说)。
首先我们当然要去下载chrome的最新版了,我的是ubuntu64bit的系统当然选择64bit.deb了,选好了点击“接受并安装”,然后就等待下载吧。

cd ~/下载
#64bit 
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb

#32bit
wget https://dl.google.com/linux/direct/google-chrome-stable_current_i386.deb

下载完成之后,我们开始安装chrome。

#为了避免提示软件依赖错误,需要libnss3-ld,我们在安装chrome前先运行
sudo apt-get -f install

cd ~/下载

sudo dpkg -i google-chrome-stable_current_amd64.deb

到这里你可以点击Dash主页,搜索chrome看看是否可以用了?

安装goagent

goagent是一款非常好用的FQ软件,在CN这个软件可以给我们看到外面世界,而且free,而且跨平台无论你是windows/linux/ios/osx/android都令你尽情畅游互联网,享受无国界的上网体验。

当然我这里只是提供gogent在ubuntu环境下的安装介绍,安装goagent的前期工作我们是要做足的,因为goagent必须依赖于python2+

sudo apt-get install python-dev python-greenlet python-gevent python-vte python-openssl python-crypto python-appindicator

#安装gevent(提升多线程性能,强烈建议安装)
sudo apt-get install python-dev python-pip && sudo pip install gevent --upgrade

#安装是否成功,你现在可以试试python的命令,如果显示出版本号,那么至此应该可以算是python装完了
python --version

前期工作做完了,现在下载goagent的最新版,目前googlecode上最新是3.1.5。

cd ~/下载
wget https://nodeload.github.com/goagent/goagent/legacy.zip/3.0 -O goagent.zip

#解压
unzip goagent.zip
#建议将goagent放在/opt目录,非强制,完全自定义
sudo mv goagent-goagent-* /opt/goagent

cd /opt/goagent/server
#上传appid
python uploader.py

#根据提示输入你自己创建的appid(若要同时上传多appid在appid之间用|隔开)和你的Gmail帐号和密码(如果开启了两步验证,密码为16位的应用程序专用密码)
#上传完成后不要忘记修改local/proxy.ini文件中的appid
#如果出现无法执行的情况,请检查goagent目录权限

goagent的服务端程序配置完成了,现在再配置它的客户端。

cd /opt/goagent/local

#运行goagent 在命令行下
python proxy.py

#运行goagent以托盘形式(可选)
python /opt/goagent/local/goagent-gtk.py

#以开启自启动(可选)
sudo vi /etc/rc.local

#在exit 0前插入
/opt/goagent/local

退出goagent

如果是直接终端使用”python proxy.py”运行,在终端按”Ctrl+C”组合键可终止运行;如果使用gtk托盘,在托盘图标上右键菜单有退出选项。直接关闭终端窗口也会退出。如果以后台进程运行,先用”ps aux | grep proxy.py”找到goagent的PID,然后直接kill对应的PID 。

ps aux|grep proxy.py|grep -v "grep"|awk '{print $2}'|xargs kill

关于goagent证书导入问题,如果你sudo提权后是可以自动导入证书,但是我的chrome貌似不行,只能使用手工导入CA.crt证书。

1、打开chrome
2、设置
3、显示高级设计
4、HTTPS/SSL(管理证书)
5、证书管理器
6、授权中心
7、点击“导入”,选择到/opt/goagent/local/CA.crt。出现三个可选框全部勾选保存,重启chrome即可。
8、测试访问下www.facebook.com

PS:一个goagent小技巧,利用goagent作为代理终端下载一些访问比较慢的update包,你应该有过这样的体会,一些国外的源在国内访问时常被终端,我们可以利用goagent来代理我们在终端所访问的资源。

#代理http
export http_proxy=http://127.0.0.1:8087

#代理https
export https_proxy=http://127.0.0.1:8087

安装xampp

前期我们也算完成一些应用软件吧,既然作为桌面系统来开发php,xampp是不二的选择,好处嘛就是下载一个就等于下载了apache+php+mysql+ftp了,是不是很爽?当然在生产环境下我们还是讲究的是专一性,用到的才安装。但是我这是桌面系统pc,不是服务器,所以xampp是我首选的目标了。
废话不说太多,先去下载xampp这个是必须的。

#如果你用了goagent代理了,呵呵你会发现这个的下载速度很快
wget http://superb-dca2.dl.sourceforge.net/project/xampp/XAMPP%20Linux/1.8.3/xampp-linux-x64-1.8.3-3-installer.run

sudo chmod 755 xampp-linux-*-installer.run

#开始安装 你会发现弹出一个GUI界面来给你安装
sudo ./xampp-linux-*-installer.run

#安装完成后xampp默认安装在/opt/lamp中 执行下面这条命令启动xampp
sudo /opt/lampp/lampp start

#如果你启动成功了,你用浏览器访问下http://localhost是否看到xampp欢迎界面

#停止xampp
sudo /opt/lampp/lampp stop

#更多的命令你可以试试
/opt/lampp/lampp -h

如果你还是更多的疑问你可以参考xampp的FAQ

安装ZendStudio

话说vim是最牛X的编辑器,但是对于我这种一直偏爱zendstudio作为我的IDE怎么舍得把它扔掉?
vim是后话,但是本次还是有很多童鞋喜欢zendstudio,所以还是不能跑题继续开始我的zendstudio安装说明,第一步当然是去下载zendstudio了。

cd ~/下载
#64bit zendstudio 10.6.0
wget http://downloads.zend.com/studio-eclipse/10.6.0/ZendStudio-10.6.0-linux.gtk.x86_64.tar.gz

#32bit zendstudio 10.6.0 
wget http://downloads.zend.com/studio-eclipse/10.6.0/ZendStudio-10.6.0-linux.gtk.x86.tar.gz

我下载的64bit的zendstudio,然后就是解压过程,将zendstudio移动到/opt/zendstudio文件夹下,由于zendstudio是基于eclipse开发的,当然需要jre的支持了,去java.com中下载java吧。下载完成后,在/opt/zendstudio目录中新建一个jre目录,将下载好的java压缩包中的文件复制到jre中即可。

cd /opt/zendstudio

./ZendStudio

至此,zendstudio已成功安装,你可以设置快捷方式到桌面。关于破解zendstudio请参见《首发Zend Studio 10.6正式版注册破解》。

安装WineQQ

WineQQ?其实就是在wine的支撑下能在linux下面运行windows的QQ版本,没有办法在中国im界绝对是QQ垄断了,在linux上面之前有官方提供的版本,但是目前官方似乎已经停止了linux-qq的维护了(当然,webQQ也应该能满足大多数的需求了),为了能在linux上面运行QQ,首先我们得要安装支撑软件wine。

sudo add-apt-repository ppa:ubuntu-wine/ppa

sudo apt-get update

sudo apt-get install wine1.7
#执行安装wine命令需要几分钟的时间,请耐心等待……
#中间会出现一个让你点击确定按钮的界面,请留意

安装完成后请在你的dash搜索wine看看出现酒杯没有?
下面我们开始安装wineQQ,longene论坛提供的qq2013sp6它们说挺好的,但是我测试了使用视频不稳定直接崩溃,其他的貌似还没有发现什么问题。

cd ~/下载
wget http://www.longene.org/download/WineQQ2013-20131120-Longene.deb

#如果之前安装过旧版本需要先卸载(通过dpkg -l | grep qq查看)。
sudo dpkg -i WineQQ2013-20131120-Longene.deb

#如果你的ubuntu和我一样是64bit操作系统的话,那么你还需要运行
sudo apt-get install ia32-libs

至此wineQQ安装完成。

高性能PHP图片动态裁剪方案2

许久以前我写过一篇也是关于高性能PHP图片动态裁剪方案的文章,那篇文章使用的是nginx Cache和rewrite实现的,当然再加上CDN,在生产环境中使用了有两个月左右,我慢慢的发现那个方案还是不能令我满意,如果没有CDN的同学那将会是一场噩梦,因为缓存设置的不好那么将会一直去请求php动态文件,那将会非常的糟糕访问量大的话IO和cpu都难受。

利用空余时间,根据这篇文章思路,新增了静态生成图片支持,同时支持对图片3种模式切换(模式0则是强制裁剪、模式1等比缩放裁剪、模式2等比放大裁剪),对于一些同学在做一些资讯网站或者对图片需求比较大的网站(比如经常存在缩略图裁剪等业务需求等),通过程序硬性的裁剪显然是一件吃力不讨好的事情,而且也违背了一处写好多处调用的思想。好了不扯很多废话,这篇文章所使用Imagick组件做为本次裁剪的底层支撑,具体的安装过程我就不赘述了不熟悉的童鞋请参考linux+windows安装php_imagick扩展

思路简述:

1、先写好请求服务器生成图片动态脚本,主要就是对图片进行等比缩放计算+裁剪。

2、确定你想要生成的url规则,比如http://www.domain.com/www/300×200-1/test.jpg。

3、对浏览器(CDN)做缓存处理。

4、结束。

这是一个利用Imagick动态裁剪图片的php脚本

/**
 * Author pony_chiang
 * 高性能图像裁剪方案
 * 需要php-imagick扩展
 */
ini_set ( "memory_limit", "80M" );

// 请求地址比如  http://yourdomain.com/resize.php?site=www&width=300&height=200&mode=2&path=uploadfile/helloworld.png
// nginx重写规则  rewrite /s/(.*)/(\d+)x(\d+)-(\d)/(.*) /s/resize.php?site=$1&width=$2&height=$3&mode=$4&path=$5 last;

$path = trim ( $_GET ['path'] );
$mode = intval ( $_GET ['mode'] );
$site = trim ( $_GET ['site'] );
$width = intval ( $_GET ['width'] );
$height = intval ( $_GET ['height'] );

$site_list = array ('www' => '/mnt/webroot/test/' );

$orig_dir = dirname ( __FILE__ );
if (! array_key_exists ( $site, $site_list )) {
	header ( 'HTTP/1.1 400 Bad Request' );
	exit ();
}

if ($mode > 3 || $mode < 0) {
	header ( 'HTTP/1.1 400 Bad Request' );
	exit ();
}

$orig_file = $site_list [$site] . $path;
if (! file_exists ( $orig_file )) {
	header ( 'HTTP/1.1 404 Not Found' );
	exit ();
}

$file_ext = '.' . pathinfo ( $path, PATHINFO_EXTENSION );

$file_name = basename ( $path, $file_ext );
$save_path = "{$orig_dir}/{$site}/{$width}x{$height}-{$mode}/{$path}";
$save_dir = dirname ( $save_path );

if (! file_exists ( $save_dir ))
	wpx_mkdir ( $save_dir );

$target_width = $width;
$target_height = $height;

$new_width = $target_width;
$new_height = $target_height;
$image = new Imagick ( $orig_file );
list ( $orig_width, $orig_height, $type, $attr ) = getimagesize ( $orig_file );

if ($mode == "0") {
	//等比缩放图像
	$new_height = $orig_height * $new_width / $orig_width;
	if ($new_height > $target_height) {
		$new_width = $orig_width * $target_height / $orig_height;
		$new_height = $target_height;
	}
} else if ($mode == "2") {
	// 放大并裁剪图像
	$desired_aspect = $target_width / $target_height;
	$orig_aspect = $orig_width / $orig_height;

	if ($desired_aspect > $orig_aspect) {
		$trim = $orig_height - ($orig_width / $desired_aspect);
		$image->cropImage ( $orig_width, $orig_height - $trim, 0, $trim / 2 );
		error_log ( "HEIGHT TRIM $trim" );
	} else {
		$trim = $orig_width - ($orig_height * $desired_aspect);
		$image->cropImage ( $orig_width - $trim, $orig_height, $trim / 2, 0 );
	}
}

$image->resizeImage ( $new_width, $new_height, imagick::FILTER_LANCZOS, 1 );
$image->writeImage ( $save_path );
header ( 'Content-Type: image/jpeg' );
header ( 'Last-Modified: ' . gmdate ( 'D, d M Y H:i:s' ) . ' GMT' );
echo file_get_contents ( $save_path );
return true;

// 循环生成目录
function wpx_mkdir($dir, $mode = 0777) {
	if (is_dir ( $dir ) || @mkdir ( $dir, $mode ))
		return true;
	if (! wpx_mkdir ( dirname ( $dir ), $mode ))
		return false;
	return @mkdir ( $dir, $mode );
}

再附上nginx.conf

server {
        listen       80;
        server_name sim.markdream.com;
        root   /mnt/webroot/test;
        index  index.php;
        expires 30d;

        location /s {
           #只有当没有生成这张图片时才调用动态裁剪
           if (!-e $request_filename) {
             rewrite /s/(.*)/(\d+)x(\d+)-(\d)/(.*) /s/resize.php?site=$1&width=$2&height=$3&mode=$4&path=$5 last;
             break;
           }
        }

        error_page   404 403 402 500 502 503 504  /404.html;
        location = /404.html {
        }

        location ~ \.php$ {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }

    }

请观看令人愉悦的demo

test-image
test-image-200×300-0

 

test-image-100x200-1
test-image-100×200-1

 

test-image-100x100-2
test-image-100×100-2

 

特别强调

PS:在文章的末尾我要特别强调一点是关于浏览器缓存的文章,如果你使用了CDN那么还是建议你在php文件中添加一个修改日期的头信息,这样cdn再第一次获取这个图片的时候虽然你服务器是刚刚生成的图片,但是cdn认为这个图片早已存在了。

header('Last-Modified: ' .gmdate('D, d M Y H:i:s') . ' GMT' );

对你使用CDN有十分莫大的帮助。具体产生的效果就是客户端第一次访问此文件的http状态码是200,刷新后状态码一直都是304了。