标签:
[我的另外一个博客地址:http://www.ncmacker.cc,那边排版会比较好点。]
公司的打印机最近新添加一个打印图片的功能,具体是用户在后台设置他的logo,服务器接收到图片文件,转换单色的bmp文件(热敏打印机),再由php将单色bmp图片转换成bin文件,里面包含了图片的头信息和数据。做这么个小功能还花了我不少时间,走了很多弯路,各位看官请容我一一讲述吧。
最开始的时候,认为这个操作php语言无法胜任,于是选用了php extension。可是作为菜鸟的我,何曾玩过这么高大上的技能?没办法了,只好现学现卖了,最开始我以为是只要用c语言将转换的功能实现了,然后封装下就可以给php调用,做到后面才发现很多c语言的函数都不能直接使用,要么是被阉割掉了,要么就是被zend重写了,只有遵循zend_api才能够做出一个有用的php扩展。网上搜索了很多关于php extension的资料,发现中文资料还是相对较少。虽然有那么几个稍微系统点的资料:
这本书的是翻译的一本国外一个大牛的扩展开发的书籍,翻译的还是比较有水准的,这个翻译项目的发起人也是大牛,其中最为知名的应该是鸟哥,如果连鸟哥都不知道的话,你这php程序员估计也是白当了。
还有极客学院也有一份这本书,与上面介绍的差不多。还有这本深入理解php内核:Thinking In PHP Int ernals也不错。还有一些国外牛人整理的一些资料,先不再这里罗嗦,待会放在结尾当福利发送哈。
我的php extension第一步是先搭建环境,我也把搭建环境的步骤也讲下吧,这样也方便其他需要使用到扩展的童鞋。
cd /home/pchangl/php-5.4.41/ext
然后在主文件夹下创建一个extension_def的目录(这个目录不是必须的,只是我为了方便管理.def的文件才建立的),在这个文件夹下建立一个叫做pchangl.def的文件(def的文件是扩展方法的签名)。
root@pchangl:/home/pchangl/php-5.4.41/ext #ext_skel --extname=pchangl --proto=/home/pchangl/php-extension/pchangl.def
这个时候就给我们生成了扩展的框架:
这个时候我们的框架已经生成了,我们先把config.m4文件修改下,删除掉第16,17,18行前的dnl注释
这个时候我们就可以修改pchangl.c的代码了,这个文件里的代码就是我们自己的程序代码。
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_pchangl.h"
static int le_pchangl;
const zend_function_entry pchangl_functions[] = {
PHP_FE(confirm_pchangl_compiled, NULL) /* For testing, remove later. */
PHP_FE_END /* Must be the last line in pchangl_functions[] */
};
zend_module_entry pchangl_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
"pchangl",
pchangl_functions,
PHP_MINIT(pchangl),
PHP_MSHUTDOWN(pchangl),
PHP_RINIT(pchangl), /* Replace with NULL if there‘s nothing to do at request start */
PHP_RSHUTDOWN(pchangl), /* Replace with NULL if there‘s nothing to do at request end */
PHP_MINFO(pchangl),
#if ZEND_MODULE_API_NO >= 20010901
PHP_PCHANGL_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_PCHANGL
ZEND_GET_MODULE(pchangl)
#endif
PHP_MINIT_FUNCTION(pchangl)
{
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(pchangl)
{
return SUCCESS;
}
PHP_RINIT_FUNCTION(pchangl)
{
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(pchangl)
{
return SUCCESS;
}
PHP_MINFO_FUNCTION(pchangl)
{
php_info_print_table_start();
php_info_print_table_header(2, "pchangl support", "enabled");
php_info_print_table_end();
}
PHP_FUNCTION(confirm_pchangl_compiled)
{
char *arg = NULL;
int arg_len, len;
char *strg;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
return;
}
len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "pchangl", arg);
RETURN_STRINGL(strg, len, 0);
}
上面的这个代码是ext_skel工具生成的框架,你写的扩展也要按照他的标准来写,由于代码长度的原因,我删除掉了一部分生成的注释,如果想看的话,你可以自己编写个框架看看注视,下面我就讲解下几个比较重要的部分吧。
PHP_MINIT_FUNCTION(pchangl)
{
return SUCCESS;
}
这个方法随着php在apache或者nginx中启动而诞生内存的时候,就会执行,可以把这个方法当成初始化的方法吧。
PHP_RINIT_FUNCTION(pchangl)
{
return SUCCESS;
}
这个方法会在每一个页面请求到来的时候执行。
PHP_RSHUTDOWN_FUNCTION(pchangl)
{
return SUCCESS;
}
RINIT_FUNCTION方法执行玩后,请求也处理的差不多了,这个时候就会进入这个方法,启动回收程序来收拾烂摊子。
PHP_MSHUTDOWN_FUNCTION(pchangl)
{
return SUCCESS;
}
前面该启动的也启动了,该结束的也结束了,现在该apache老人家休息的时候,当apache通知php自己要stop的时候,就会进入这个方法。给扩展下最后通碟,如果有未了的心愿,就放在这个方法里,这可是最后的机会哦
PHP_FUNCTION(function name)
{
//实现
}
这个方法就是我们扩展方法的主体了。
pchangl.c这个文件就介绍到这里了,其他的内容就到我推荐的几个资料中查询吧,都有的。我也就不一一讲解了。
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
//调试开关
#define PRINTF_DEBUG 0
//四个字节对齐 进1制处理
#define WIDTHBYTES(bits) (((bits)+31)/32*4) //每一行的像素宽度必须是4的倍数,否则补0补齐。
/*定义BYTE为无符号一个字节的类型*/
typedef unsigned char BYTE;
/*定义WORD为无符号两个字节的类型*/
typedef unsigned short WORD;
/*定义DWORD为无符号四个字节的类型*/
typedef unsigned long DWORD;
#pragma pack(1)
/*位图文件头*/
typedef struct BMP_FILE_HEADER
{
WORD bType; /* 文件标识符 */
DWORD bSize; /* 文件的大小 */
WORD bReserved1; /* 保留值,必须设置为0 */
WORD bReserved2; /* 保留值,必须设置为0 */
DWORD bOffset; /* 文件头的最后到图像数据位开始的偏移量 */
} BMPFILEHEADER;
/*位图信息头*/
typedef struct BMP_INFO
{
DWORD bInfoSize; /* 信息头的大小 */
DWORD bWidth; /* 图像的宽度 */
DWORD bHeight; /* 图像的高度 */
WORD bPlanes; /* 图像的位面数 */
WORD bBitCount; /* 每个像素的位数 */
DWORD bCompression; /* 压缩类型 */
DWORD bmpImageSize; /* 图像的大小,以字节为单位 */
DWORD bXPelsPerMeter; /* 水平分辨率 */
DWORD bYPelsPerMeter; /* 垂直分辨率 */
DWORD bClrUsed; /* 使用的色彩数 */
DWORD bClrImportant; /* 重要的颜色数 */
} BMPINF;
typedef struct tagRGBQUAD
{
BYTE rgbBlue; //蓝色的亮度(值范围为0-255)
BYTE rgbGreen; //绿色的亮度(值范围为0-255)
BYTE rgbRed; //红色的亮度(值范围为0-255)
BYTE rgbReserved; //保留,必须为0
} RGBQUAD;
#pragma pack()
int main()
{
FILE *fpin = NULL;
FILE *fpout = NULL;
BMPFILEHEADER fileHeader = { 0, 0 };
BMPINF infoHeader = { 0, 0 };
RGBQUAD imgHeader[256 * 4] = { 0, 0 };
DWORD offset = 0, width = 0, height = 0, bitCount = 0;
DWORD l_width = 0, bmp_width = 0, bmp_height = 0;
DWORD i = 0, j = 0, m = 0;
BYTE *bmpDataTmp = NULL;
BYTE *bmpPixelTmp = NULL;
BYTE bmp_start[8] = "ICONTOP";
BYTE bmp_end[8] = "ICONEND";
//打开文件