[转]Linux下用C开发PHP扩展

一、首先下载PHP源码包,假设源码包目录为:/software/php-5.2.13
一、首先下载PHP源码包,假设源码包目录为:/software/php-5.2.13
#> cd /software/php-5.2.13/ext
二、假设我们要开发一个名为caleng_module的扩展,该扩展包含两个函数:a–处理两个整型相加和b-处理字符串重复输出;
1、首先编写一个函数定义文件,该文件编写函数原型后缀为def,假设为:caleng_module.def
int a(int x, int y)
string b(string str, int n)
2、通过扩展骨架生成器,将在ext目录下自动建立扩展目录caleng_module
#> ./ext_skel –extname=caleng_module –proto=caleng_module.def
3、修改配置文件: #> vim /software/php-5.2.13/ext/caleng_module/config.m4,将如下行的注释标签”dnl”去掉,修改后如下所示:
PHP_ARG_ENABLE(myfunctions, whether to enable myfunctions support,
Make sure that the comment is aligned:
[ –enable-myfunctions Enable myfunctions support])
4、完善函数a和b的功能: #> vim /software/php-5.2.13/ext/caleng_module/caleng_module.c
PHP_FUNCTION(a)
{
int x, y, z;

int argc = ZEND_NUM_ARGS();

if (zend_parse_parameters(argc TSRMLS_CC, “ll”, &x, &y) == FAILURE)
return;
z = x + y;
RETURN_LONG(z);
}
PHP_FUNCTION(b)
{
char *str = NULL;
int argc = ZEND_NUM_ARGS();
int str_len;
long n;
char *result;
char *ptr;
int result_length;

if (zend_parse_parameters(argc TSRMLS_CC, “sl”, &str, &str_len, &n) == FAILURE)
return;
result_length = str_len * n;
result = (char *) emalloc(result_length + 1);
ptr = result;
while (n–) {
memcpy(ptr, str, str_len);
ptr += str_len;
}
*ptr = ‘\0’;
RETURN_STRINGL(result, result_length, 0);
}
三、编译安装,假设php的安装目录为:/usr/localhost/webserver/php
#> cd /software/php-5.2.13/ext/caleng_module
#> /usr/localhost/webserver/php/bin/phpize
#> ./configure –with-php-config=/usr/localhost/webserver/php/bin/php-config
#> make
#> make install
现在将在/usr/local/webserver/php/lib/php/extensions/no-debug-non-zts-20060613目录下生成caleng_module.so文件
在php.ini配置文件中加入: extension=caleng_module.so.

[转]LNMP的并发考虑与资源分配

在招聘中常问的一个问题

PHPer当被问到你的程序性能如何?程序的并发可以达到多少?程序的瓶颈在哪儿?为了满足业务需求应该购买多少台服务器?负载均衡中php应用服务器需要多少台?

可能这些问题在面试中会设置一个应用的场景及一些前提条件,让面试的人去设计,并提出看法建议,能够回答得很好的人还是比较少的。

今天我们来谈谈LNMP的并发考虑和资源分配。

概念

LNMP中的N是nginx充当Web Server

内容的分发者,会在文件系统找到相应的文件,就返回给浏览器,如:nginx。如果是静态的文件,就可以直接返回,但是如果是index.php需要解析并执行的脚本文件时,Web Server就无力了,需要将请求转发给相应的脚本语言的解析器来解释并执行,最终将程序的执行结果,返回给Web Server,再返回给浏览器。

LNMP中的P是php充当后端的逻辑处理程序

那么php与nginx的常规协作方式是如何的呢?需要我们明确几个概念

cgi

通用网关接口,是HTTP协议中描述的,Web Server与后端处理程序进程间通信的协议

php-cgi

php实现了cgi协议,使得web server与php共同完成一个动态网页的请求响应

fastcgi

是为了解决cgi性能问题,而规范的另外一种协议,为什么说解决cgi性能问题,因为在面对各大中型网站的业务需求中,cgi程序表现得越来越无力,因为cgi程序在每次接收到请求时都需要启动新的进程,并初始化环境,然后执行程序,具体的协议内容,在此不引述。

php-fpm

实现了fastcgi协议,是php-cgi的进程管理器,解决高并发网站的性能问题。

在最终回答LNMP的并发考虑与资源分配还需要明确的几个概念

并发

一般由单位内完成的请求数来衡量,如,每秒事务数(TPS),每秒HTTP请求数(HPS),每秒查询数(QPS)。通常情况下,我们说PHP的并发,都是指一秒内PHP完成的动态请求的次数。如某网站高峰期的动态请求并发为5000每秒,这个数字不算太高,但也不低。一般日活跃用户数在1000万-5000万的网站应用才能达到这个级别。

性能

一般是指应用程序的处理速度,如果php的应用程序,打开一个页面(执行一个脚本程序)通常需要在50-100ms完成,这对程序的性能要求还是比较高的。但是这还仅仅只是程序处理,php处理完成之后,还要交给web server,web server再将数据返回浏览器,这中间会有一个网络延迟,通常网络正常的情况下,需要大约100ms,最终一个动态网页的请求大约200ms(理想的情况下)可以到达用户浏览器端(仅仅是一个html结构)。

资源分配

php-fpm进程数

按照上面的描述,并发为5000每秒,每个请求完成大约200ms(具体页面要具体分析,这里只是一个理想值),如果只有5台PHP应用程序服务器,那么每台机器平均为并发1000每秒,如果是使用nginx+php-fpm的架构,php-fpm的php-cgi进程管理器的配置应该如何呢?我计算的结果为(具体的配置项说明在后文):

1
2
pm=static
pm.max_children=100
上面的100是如何得来的,由于机器平均并发为1000每秒,每个动态请求的处理时间为100ms,也就是说1个php-fpm的worker处理进程在1秒内可以处理10个请求,100个php-fpm的worker处理进程,就可以处理1000个请求。

当然需要结合服务器硬件资源来进行配置,如果配置不当,很容易在请求高峰期或者流量猛增导致服务器宕机。

网络带宽

网络带宽也会是一个重要的因素,如果你的服务处理很强,但是用户的请求和响应不能及时到达也是白忙活,这个参数如何计算呢?

并发5000每秒,每个请求的输出为20K,则5000x20K=100000K=100M

这就要求你的公网负载均衡器外网出口带宽至少要达到100M

内存

上述中100个php-fpm的worker处理进程,理论上如果服务器只运行php-fpm,那么我们可以将服务器内存的一半分配给php-fpm,通常情况下,我们可以认为一个php-fpm的worker处理进程占用内存20M,那么100x20M=2G,也就是说明服务器的内存大约为4G

CPU

由于php-fpm是一个多进程的模型应用,CPU进程调度消耗也是很大的,并且PHP应用程序有问题也会导致CPU占用率高,这就没有量化的指标,需要具体情况具体分析了。但是有一个小建议,可以部署一个crontab每隔一分钟检测cpu占用率超过多少就kill掉相应的php-fpm的worker处理进程。

php-fpm Unix Socket

如果nginx与php在同一台机器,nginx与php-fpm使用unix域套接字代替tcp socke进行通信,这个配置挺关键的,纯echo的ab测试,采用unix域套接字每秒请求数提升10%-20%

即nginx中配置:

1
fastcgi_pass unix:/data/server/var/php/php-fpm.sock;
php-fpm.conf中配置:

1
listen = /data/server/var/php/php-fpm.sock
最后遇到很多同学对php-fpm的进程管理器的核心配置不太了解,下面是我翻译的配置说明:

php-fpm配置项

pm

进程管理器以控制子进程的数量,可能的值有

static                        一个固定的值,由pm.max_children指定
dynamic                       动态的(工作方式和Apache的prefork模式一致),但是保持至少一个,由
                              pm.max_children             在同一时间最大的进程数
                              pm.start_servers              php-fpm启动时开启的等待请求到来的进程数
                              pm.min_spare_servers    在空闲状态下,运行的最小进程数,如果小于此值,会创建新的进程
                              pm.max_spare_servers   在空闲状态下,运行的最大进程数,如果大于此值,会kill部分进程
ondemand                      启动时不会创建进程,当请求达到时创建子进程处理请求
                              pm.max_children 在同一时间最大的进程数
                              pm.process_idle_timeout  空闲多少秒之后进程会被kill
1
pm = static
pm.max_children

在同一时间最大的进程数

1
pm.max_children = 120
pm.start_servers

php-fpm启动时开启的等待请求到来的进程数,默认值为:min_spare_servers + (max_spare_servers – min_spare_servers) / 2

1
pm.start_servers = 80
pm.min_spare_servers

在空闲状态下,运行的最小进程数,如果小于此值,会创建新的进程

1
pm.min_spare_servers = 60
pm.max_spare_servers

在空闲状态下,运行的最大进程数,如果大于此值,会kill部分进程

1
pm.max_spare_servers = 120
pm.process_idle_timeout

空闲多少秒之后进程会被kill,默认为10s

1
pm.process_idle_timeout = 10s
pm.max_requests

每个进程处理多少个请求之后自动终止,可以有效防止内存溢出,如果为0则不会自动终止,默认为0

1
pm.max_requests = 5000
pm.status_path

注册的URI,以展示php-fpm状态的统计信息

1
pm.status_path = /status
其中统计页面信息有:

pool                         进程池名称
process manager              进程管理器名称(static, dynamic or ondemand)
start time                   php-fpm启动时间
start since                  php-fpm启动的总秒数
accepted conn                当前进程池接收的请求数
listen queue                 等待队列的请求数
max listen queue             自启动以来等待队列中最大的请求数
listen queue len             等待连接socket队列大小
idle processes               当前空闲的进程数
active processes             活动的进程数
total processes              总共的进程数(idle+active)
max active processes         自启动以来活动的进程数最大值
max children reached         达到最大进程数的次数
ping.path

ping url,可以用来测试php-fpm是否存活并可以响应

1
ping.path = /ping
ping.response

ping url的响应正文

1
ping.response = pong
#LNMP #Linux #Nginx

[转]深入解析PHP垃圾回收机制对内存泄露的处理

上次说到了refcount和is_ref,这里来说说内存泄露的情况
复制代码 代码如下:

$a = array(1, 2, &$a);
unset($a);

在老的PHP版本中,这里就会出现内存泄露,分析如下:
执行第一行,可以知道$a和$a[2]指向的zval refcount=2,is_ref=1
然后执行第二行,$a将会从符号表中被删除,同时指向的zval的refcount–,此时refcount=1,因为refcount!=0,故此zval不会被当做垃圾回收,但是此时我们却失去了$a[2]指向这个zval的入口,因此这个zval成了一块内存垃圾
同样的道理可以发生在类内部引用里,例如
复制代码 代码如下:

$a = new Man();
$a->self = &$a;
unset($a);

那么如何解决这种问题呢,新的GC机制采用了一个算法来解决这个问题
PHP有一个root buffer用来存储zval的节点信息,当root buffer满了或者手动调用gc函数时,GC算法启动
对于一个数组或者类类型的zval而言,在垃圾回收机制启动时,算法会对该zval的数组/类内部的元素/成员的zval进行一次遍历并将refcount减1,如果说遍历完成后该zval的refcount被减为0,则说明这个zval是一个内存垃圾,他将被销毁,见下面的例子
复制代码 代码如下:

$a = array(1, 2, &$a, &$a);
unset($a);

容易知道$a指向的zval,假设为z1的refcount=3,is_ref=1
当unset($a)执行的时候,$a就已经从符号表中删去,同时我们也失去了访问z1的入口,此时z1 refcount=2,is_ref=1
当GC启动时,会对该z1的数组元素的zval的refcount进行遍历减1,遍历到a[2]时,z1 refcount–, a[3]时 z1 refcount–,此时z1 refcount = 0,即可将z1标记为内存垃圾,算法后将其回收
总结来说可以这么表述:若一个数组类型的zval,对他的元素zval进行一次遍历,同时将遍历到的zval的refcount–,如果最后refcount=0的zval,就是垃圾,需要被回收

[转]PHP之写时复制介绍(Copy On Write)

原文:http://www.jb51.net/article/50080.htm
很多时候,我们会因为一些术语而对其概念产生莫测高深的恐惧,而其实,他们的基本原理往往非常简单。本小节将介绍PHP中写时复制这种策略的实现
在开始之前,我们可以先看一段简单的代码:
复制代码 代码如下:


执行这段代码,会打印出数字2。从内存的角度来分析一下这段代码“可能”是这样执行的:分配一块内存给foo变量,里面存储一个1; 再分配一块内存给bar变量,也存一个1,最后计算出结果输出。事实上,我们发现foo和bar变量因为值相同,完全可以使用同一块内存,这样,内存的使用就节省了一个1,并且,还省去了分配内存和管理内存地址的计算开销。没错,很多涉及到内存管理的系统,都实现了这种相同值共享内存的策略:写时复制
很多时候,我们会因为一些术语而对其概念产生莫测高深的恐惧,而其实,他们的基本原理往往非常简单。本小节将介绍PHP中写时复制这种策略的实现:
写时复制(Copy on Write,也缩写为COW)的应用场景非常多, 比如Linux中对进程复制中内存使用的优化,在各种编程语言中,如C++的STL等等中均有类似的应用。 COW是常用的优化手段,可以归类于:资源延迟分配。只有在真正需要使用资源时才占用资源, 写时复制通常能减少资源的占用。
注: 为节省篇幅,下文将统一使用COW来表示“写时复制”;
推迟内存复制的优化
正如前面所说,PHP中的COW可以简单描述为:如果通过赋值的方式赋值给变量时不会申请新内存来存放新变量所保存的值,而是简单的通过一个计数器来共用内存,只有在其中的一个引用指向变量的值发生变化时才申请新空间来保存值内容以减少对内存的占用。在很多场景下PHP都COW进行内存的优化。比如:变量的多次赋值、函数参数传递,并在函数体内修改实参等。
下面让我们看一个查看内存的例子,可以更容易看到COW在内存使用优化方面的明显作用:
复制代码 代码如下:

(refcount=1, is_ref=0)=’php-internal’,
1 => (refcount=1, is_ref=0)=’php-internal’,
2 => (refcount=1, is_ref=0)=’php-internal’)
copy: (refcount=2, is_ref=0)=array (0 => (refcount=1, is_ref=0)=’php-internal’,
1 => (refcount=1, is_ref=0)=’php-internal’,
2 => (refcount=1, is_ref=0)=’php-internal’)
int(629512)
tipi: (refcount=1, is_ref=0)=array (0 => (refcount=1, is_ref=0)=’php-internal’,
1 => (refcount=2, is_ref=0)=’php-internal’,
2 => (refcount=2, is_ref=0)=’php-internal’)
copy: (refcount=1, is_ref=0)=array (0 => (refcount=1, is_ref=0)=’php-internal’,
1 => (refcount=2, is_ref=0)=’php-internal’,
2 => (refcount=2, is_ref=0)=’php-internal’)
int(630088)
在这个例子中,我们可以发现以下特点:
$copy = $tipi;这种基本的赋值操作会触发COW的内存“共享”,不会产生内存复制;
COW的粒度为zval结构,由PHP中变量全部基于zval,所以COW的作用范围是全部的变量,而对于zval结构体组成的集合(如数组和对象等),在需要复制内存时,将复杂对象分解为最小粒度来处理。这样可以使内存中复杂对象中某一部分做修改时,不必将该对象的所有元素全部“分离复制”出一份内存拷贝;
复制代码 代码如下:

array_fill()填充数组时也采用了COW的策略,可能会影响对本例的演示,感兴趣的读者可以 阅读:$PHP_SRC/ext/standard/array.c中PHP_FUNCTION(array_fill)的实现。

xdebug_debug_zval()是xdebug扩展中的一个函数,用于输出变量在zend内部的引用信息。 如果你没有安装xdebug扩展,也可以使用debug_zval_dump()来代替。 参考:http://www.php.net/manual/zh/function.debug-zval-dump.php
实现写时复制
看完上面的三个例子,相信大家也可以了解到PHP中COW的实现原理: PHP中的COW基于引用计数ref_count和is_ref实现,多一个变量指针,就将ref_count加1, 反之减去1,减到0就销毁;同理,多一个强制引用&,就将is_ref加1,反之减去1。
这里有一个比较典型的例子:
复制代码 代码如下:


//—–执行结果—–
foo: (refcount=1, is_ref=0)=1
foo: (refcount=2, is_ref=0)=1
foo: (refcount=1, is_ref=0)=1
经过前面对变量章节的介绍,我们知道当$foo被赋值时,$foo变量的值的只由$foo变量指向。当$foo的值被赋给$bar时,PHP并没有将内存复制一份交给$bar,而是把$foo和$bar指向同一个地址。同时引用计数增加1,也就是新的2。随后,我们更改了$bar的值,这时如果直接需该$bar变量指向的内存,则$foo的值也会跟着改变。这不是我们想要的结果。于是,PHP内核将内存复制出来一份,并将其值更新为赋值的:2(这个操作也称为变量分离操作),同时原$foo变量指向的内存只有$foo指向,所以引用计数更新为:refcount=1。
看上去很简单,但由于&运算符的存在,实际的情形要复杂的多。见下面的例子:

图6.6 &操作符引起的内存复制分离>
从这个例子可以看出PHP对&运算符的一个容易出问题的处理:当 $beauty=&$pan; 时,两个变量本质上都变成了引用类型,导致看上去的普通变量$pan, 在某些内部处理中与&$pan行为相同,尤其是在数组元素中使用引用变量,很容易引发问题。(见最后的例子)
PHP的大多数工作都是进行文本处理,而变量是载体,不同类型的变量的使用贯穿着PHP的生命周期,变量的COW策略也就体现了Zend引擎对变量及其内存处理,具体可以参阅源码文件相关的内容:
复制代码 代码如下:

Zend/zend_execute.c
========================================
zend_assign_to_variable_reference();
zend_assign_to_variable();
zend_assign_to_object();
zend_assign_to_variable();

//以及下列宏定义的使用
Zend/zend.h
========================================
#define Z_REFCOUNT(z) Z_REFCOUNT_P(&(z))
#define Z_SET_REFCOUNT(z, rc) Z_SET_REFCOUNT_P(&(z), rc)
#define Z_ADDREF(z) Z_ADDREF_P(&(z))
#define Z_DELREF(z) Z_DELREF_P(&(z))
#define Z_ISREF(z) Z_ISREF_P(&(z))
#define Z_SET_ISREF(z) Z_SET_ISREF_P(&(z))
#define Z_UNSET_ISREF(z) Z_UNSET_ISREF_P(&(z))
#define Z_SET_ISREF_TO(z, isref) Z_SET_ISREF_TO_P(&(z), isref)
最后,请慎用引用&
引用和前面提到的变量的引用计数和PHP中的引用并不是同一个东西,引用和C语言中的指针的类似,他们都可以通过不同的标示访问到同样的内容,但是PHP的引用则只是简单的变量别名,没有C指令的灵活性和限制。
PHP中有非常多让人觉得意外的行为,有些因为历史原因,不能破坏兼容性而选择暂时不修复,或者有的使用场景比较少。在PHP中只能尽量的避开这些陷阱。例如下面这个例子。
由于引用操作符会导致PHP的COW策略优化,所以使用引用也需要对引用的行为有明确的认识才不至于误用,避免带来一些比较难以理解的的Bug。如果您认为您已经足够了解了PHP中的引用,可以尝试解释下面这个例子:
复制代码 代码如下:

[php]使用ReflectionClass类

今天使用了获取类中常量的方法

$r = new ReflectionClass($this);
$ret = $r->getConstants();

其中$this参数为一个类对象.
手册中还有很多这个类的其他方法,简单好用

ReflectionClass implements Reflector {
/* 常量 */
const integer IS_IMPLICIT_ABSTRACT = 16 ;
const integer IS_EXPLICIT_ABSTRACT = 32 ;
const integer IS_FINAL = 64 ;
/* 属性 */
public $name ;
/* 方法 */
public __construct ( mixed $argument )
public static string export ( mixed $argument [, bool $return = false ] )
public mixed getConstant ( string $name )
public array getConstants ( void )
public ReflectionMethod getConstructor ( void )
public array getDefaultProperties ( void )
public string getDocComment ( void )
public int getEndLine ( void )
public ReflectionExtension getExtension ( void )
public string getExtensionName ( void )
public string getFileName ( void )
public array getInterfaceNames ( void )
public array getInterfaces ( void )
public ReflectionMethod getMethod ( string $name )
public array getMethods ([ int $filter ] )
public int getModifiers ( void )
public string getName ( void )
public string getNamespaceName ( void )
public object getParentClass ( void )
public array getProperties ([ int $filter ] )
public ReflectionProperty getProperty ( string $name )
public string getShortName ( void )
public int getStartLine ( void )
public array getStaticProperties ( void )
public mixed getStaticPropertyValue ( string $name [, mixed &$def_value ] )
public array getTraitAliases ( void )
public array getTraitNames ( void )
public array getTraits ( void )
public bool hasConstant ( string $name )
public bool hasMethod ( string $name )
public bool hasProperty ( string $name )
public bool implementsInterface ( string $interface )
public bool inNamespace ( void )
public bool isAbstract ( void )
public bool isCloneable ( void )
public bool isFinal ( void )
public bool isInstance ( object $object )
public bool isInstantiable ( void )
public bool isInterface ( void )
public bool isInternal ( void )
public bool isIterateable ( void )
public bool isSubclassOf ( string $class )
public bool isTrait ( void )
public bool isUserDefined ( void )
public object newInstance ( mixed $args [, mixed $... ] )
public object newInstanceArgs ([ array $args ] )
public object newInstanceWithoutConstructor ( void )
public void setStaticPropertyValue ( string $name , string $value )
public string __toString ( void )
}

其他的方法为:
• ReflectionClass::__construct — 构造一个 ReflectionClass 类
• ReflectionClass::export — 导出一个类
• ReflectionClass::getConstant — 获取定义过的一个常量
• ReflectionClass::getConstants — 获取一组常量
• ReflectionClass::getConstructor — 获取类的构造函数
• ReflectionClass::getDefaultProperties — 获取默认属性
• ReflectionClass::getDocComment — 获取文档注释
• ReflectionClass::getEndLine — 获取最后一行的行数
• ReflectionClass::getExtension — 根据已定义的类获取所在扩展的 ReflectionExtension 对象
• ReflectionClass::getExtensionName — 获取定义的类所在的扩展的名称
• ReflectionClass::getFileName — 获取定义类的文件名
• ReflectionClass::getInterfaceNames — 获取接口(interface)名称
• ReflectionClass::getInterfaces — 获取接口
• ReflectionClass::getMethod — 获取一个类方法的 ReflectionMethod。
• ReflectionClass::getMethods — 获取方法的数组
• ReflectionClass::getModifiers — 获取修饰符
• ReflectionClass::getName — 获取类名
• ReflectionClass::getNamespaceName — 获取命名空间的名称
• ReflectionClass::getParentClass — 获取父类
• ReflectionClass::getProperties — 获取一组属性
• ReflectionClass::getProperty — 获取类的一个属性的 ReflectionProperty
• ReflectionClass::getShortName — 获取短名
• ReflectionClass::getStartLine — 获取起始行号
• ReflectionClass::getStaticProperties — 获取静态(static)属性
• ReflectionClass::getStaticPropertyValue — 获取静态(static)属性的值
• ReflectionClass::getTraitAliases — 返回 trait 别名的一个数组
• ReflectionClass::getTraitNames — 返回这个类所使用 traits 的名称的数组
• ReflectionClass::getTraits — 返回这个类所使用的 traits 数组
• ReflectionClass::hasConstant — 检查常量是否已经定义
• ReflectionClass::hasMethod — 检查方法是否已定义
• ReflectionClass::hasProperty — 检查属性是否已定义
• ReflectionClass::implementsInterface — 接口的实现
• ReflectionClass::inNamespace — 检查是否位于命名空间中
• ReflectionClass::isAbstract — 检查类是否是抽象类(abstract)
• ReflectionClass::isCloneable — 返回了一个类是否可复制
• ReflectionClass::isFinal — 检查类是否声明为 final
• ReflectionClass::isInstance — 检查类的实例
• ReflectionClass::isInstantiable — 检查类是否可实例化
• ReflectionClass::isInterface — 检查类是否是一个接口(interface)
• ReflectionClass::isInternal — 检查类是否由扩展或核心在内部定义
• ReflectionClass::isIterateable — 检查是否可迭代(iterateable)
• ReflectionClass::isSubclassOf — 检查是否为一个子类
• ReflectionClass::isTrait — 返回了是否为一个 trait
• ReflectionClass::isUserDefined — 检查是否由用户定义的
• ReflectionClass::newInstance — 从指定的参数创建一个新的类实例
• ReflectionClass::newInstanceArgs — 从给出的参数创建一个新的类实例。
• ReflectionClass::newInstanceWithoutConstructor — 创建一个新的类实例而不调用它的构造函数
• ReflectionClass::setStaticPropertyValue — 设置静态属性的值
• ReflectionClass::__toString — 返回 ReflectionClass 对象字符串的表示形式。

[php]$HTTP_RAW_POST_DATA与$_POST

手册中说明:
总是产生变量包含有原始的 POST 数据。否则,此变量仅在碰到未识别 MIME 类型的数据时产生。不过,访问原始 POST 数据的更好方法是 php://input。$HTTP_RAW_POST_DATA 对于 enctype=”multipart/form-data” 表单数据不可用。
问题: $HTTP_RAW_POST_DATA == $_POST 吗?
照手册所写 ,答案应该就为否。
假如不一样的话,他们的区别是什么呢?
如下:
The RAW / uninterpreted HTTP POst information can be accessed with:
$GLOBALS[‘HTTP_RAW_POST_DATA’]
This is useful in cases where the post Content-Type is not something PHP understands (such as text/xml).
也就是说,基本上$GLOBALS[‘HTTP_RAW_POST_DATA’] 和 $_POST是一样的。但是如果post过来的数据不是PHP能够识别的,你可以用 $GLOBALS[‘HTTP_RAW_POST_DATA’]来接收,比如 text/xml 或者 soap 等等。
PHP默认识别的数据类型是application/x-www.form-urlencoded标准的数据类型。

        var_dump($GLOBALS['HTTP_RAW_POST_DATA']);   //xml
        $data = file_get_contents("php://input", "r"); //xml
        var_dump($data);
var_dump($_POST); //普通post
 

[前端]less的使用及和css的相互转换

关于less请看官方文档:http://www.1024i.com/demo/less/
一 基本用途
LESSCSS是一种动态样式语言,属于CSS预处理语言的一种,它使用类似CSS的语法,为CSS的赋予了动态语言的特性,如变量、继承、运算、函数等,更方便CSS的编写和维护。

LESSCSS可以在多种语言、环境中使用,包括浏览器端、桌面客户端、服务端。

语言特性快速预览:

变量:

变量允许我们单独定义一系列通用的样式,然后在需要的时候去调用。所以在做全局样式调整的时候我们可能只需要修改几行代码就可以了。

LESS源码:

@color: #4D926F;

#header {
color: @color;
}
h2 {
color: @color;
}
编译后的CSS:

#header {
color: #4D926F;
}
h2 {
color: #4D926F;
}
混合(Mixins)

混合可以将一个定义好的class A轻松的引入到另一个class B中,从而简单实现class B继承class A中的所有属性。我们还可以带参数地调用,就像使用函数一样。

LESS源码:

.rounded-corners (@radius: 5px) {
-webkit-border-radius: @radius;
-moz-border-radius: @radius;
-ms-border-radius: @radius;
-o-border-radius: @radius;
border-radius: @radius;
}

#header {
.rounded-corners;
}
#footer {
.rounded-corners(10px);
}
编译后的CSS:

#header {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-ms-border-radius: 5px;
-o-border-radius: 5px;
border-radius: 5px;
}
#footer {
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
-ms-border-radius: 10px;
-o-border-radius: 10px;
border-radius: 10px;
}
嵌套

我们可以在一个选择器中嵌套另一个选择器来实现继承,这样很大程度减少了代码量,并且代码看起来更加的清晰。

LESS源码:

#header {
h1 {
font-size: 26px;
font-weight: bold;
}
p {
font-size: 12px;
a {
text-decoration: none;
&:hover {
border-width: 1px
}
}
}
}
编译后的CSS:

#header h1 {
font-size: 26px;
font-weight: bold;
}
#header p {
font-size: 12px;
}
#header p a {
text-decoration: none;
}
#header p a:hover {
border-width: 1px;
}
函数和运算

运算提供了加,减,乘,除操作;我们可以做属性值和颜色的运算,这样就可以实现属性值之间的复杂关系。LESS中的函数一一映射了JavaScript代码,如果你愿意的话可以操作属性值。

LESS源码:

@the-border: 1px;
@base-color: #111;
@red: #842210;

#header {
color: (@base-color * 3);
border-left: @the-border;
border-right: (@the-border * 2);
}
#footer {
color: (@base-color + #003300);
border-color: desaturate(@red, 10%);
}
编译后的CSS:

#header {
color: #333;
border-left: 1px;
border-right: 2px;
}
#footer {
color: #114411;
border-color: #7d2717;
}
二 使用
1,nodejs转换
Node.js库

LESSCSS官方有一款基于Node.js的库,用于编译.less文件。

使用时,首先全局安装less(部分系统下可能需要在前面加上sudo切换为超级管理员权限):

npm install -g less
接下来就可以使用lessc来编译.less文件了:

lessc example/example.less example/example.css
2,浏览器解析
LESSCSS也可以不经编译,直接在浏览器端使用。

使用方法:

下载LESSCSS的.js文件,例如lesscss-1.4.0.min.js。
在页面中引入.less文件

需要注意rel属性的值是stylesheet/less,而不是stylesheet。

引入第1步下载的.js文件


需要特别注意的是,由于浏览器端使用时是使用ajax来拉取.less文件,因此直接在本机文件系统打开(即地址是file://开头)或者是有跨域的情况下会拉取不到.less文件,导致样式无法生效。
关于浏览器解析会存在很多意想不到的坑,所以一般是将其转换成css文件
三 nodejs自动转换
简化指令参数:
1. 使用node.js的path包拼接文件路径:

Js代码 收藏代码

//js file path is ${project}/bin/less2.js  
var path = require('path');  
  
var public_dir = path.join(__dirname, "..", "public");  
var less_dir = path.join(public_dir, "less", "base.less");  
var css_dir = path.join(public_dir, "css", "base.css");  

2. 使用exec模块调用shell

Js代码 收藏代码

var exec = require('child_process').exec;  
  
function compile_less(input_file, output_file) {  
    var cmd = ['lessc ', input_file, ' > ', output_file].join('');  
    exec(cmd, {encoding: 'utf-8'},   
        function(error, stdout, stderr) {  
            if(error !== null) {  
                console.log(error);  
                return;  
            }  
            console.log(stdout);  
        });  
}  
  
console.log('compile ' + less_dir + ' once...');  
compile_less(less_dir, css_dir);  

3. 监视文件改动

Js代码 收藏代码

var   
fs = require('fs');  
  
console.log('watching file ...');  
fs.watchFile(less_dir, {   
        persistent: true,   
        interval: 1000 // 1 sec  
    },   
    function(curr, prev) {  
        console.log('the file changed, compile ...');  
        compile_less(less_dir, css_dir);  
    });  

*并没有亲自实践

[转]Linux Crontab 安装使用详细说明

crontab命令常见于Unix和Linux的操作系统之中,用于设置周期性被执行的指令。该命令从标准输入设备读取指令,并将其存放于“crontab”文件中,以供之后读取和执行。通常,crontab储存的指令被守护进程激活。crond 常常在后台运行,每一分钟检查是否有预定的作业需要执行。这类作业一般称为cron jobs。

一、安装

[root@CentOS ~]# yum -y install vixie-cron
[root@CentOS ~]# yum -y install crontabs

说明:
vixie-cron 软件包是 cron 的主程序;
crontabs 软件包是用来安装、卸装、或列举用来驱动 cron 守护进程的表格的程序。

二、配置

cron 是 linux 的内置服务,但它不自动起来,可以用以下的方法启动、关闭这个服务:
service crond start //启动服务
service crond stop //关闭服务
service crond restart //重启服务
service crond reload //重新载入配置
service crond status //查看crontab服务状态

在CentOS系统中加入开机自动启动: chkconfig –level 345 crond on

cron 的主配置文件是 /etc/crontab,它包括下面几行:

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# run-parts
01 * * * * root run-parts /etc/cron.hourly
02 4 * * * root run-parts /etc/cron.daily
22 4 * * 0 root run-parts /etc/cron.weekly
42 4 1 * * root run-parts /etc/cron.monthly

前四行是用来配置 cron 任务运行环境的变量。
SHELL 变量的值告诉系统要使用哪个 shell 环境(在这个例子里是 bash shell);
PATH 变量定义用来执行命令的路径。
cron 任务的输出被邮寄给 MAILTO 变量定义的用户名。
如果 MAILTO 变量被定义为空白字符串(MAILTO=””),电子邮件就不会被寄出。
HOME 变量可以用来设置在执行命令或脚本时使用的主目录。

限制对 cron 的使用:

/etc/cron.allow和/etc/cron.deny 文件被用来限制对 cron 的使用。
这两个使用控制文件的格式都是每行一个用户。
两个文件都不允许空格。
如果使用控制文件被修改了,cron 守护进程(crond)不必被重启。
使用控制文件在每次用户添加或删除一项 cron 任务时都会被读取。

无论使用控制文件中的规定如何,root 都总是可以使用 cron。

如果 cron.allow 文件存在,只有其中列出的用户才被允许使用 cron,并且 cron.deny 文件会被忽略。
如果 cron.allow 文件不存在,所有在 cron.deny 中列出的用户都被禁止使用 cron。

三、crontab 命令

功能:设置计时器。

语法:crontab[-u <用户名称>][配置文件] 或 crontab [-u <用户名称>][-elr]
解释:cron 是一个常驻服务,它提供计时器的功能,让用户在特定的时间得以执行预设的指令或程序。只要用户会编辑计时器的配置文件,就可以使 用计时器的功能。其配置文件格式如下:Minute Hour Day Month DayOFWeek Command

参数:
-e 编辑该用户的计时器设置。
-l 列出该用户的计时器设置。
-r 删除该用户的计时器设置。
-u<用户名称>  指定要设定计时器的用户名称。

格式:
* *  *  *  *  command
分 时 日 月 周  命令

第1列表示分钟1~59 每分钟用*或者 */1表示
第2列表示小时1~23(0表示0点)
第3列表示日期1~31
第4列表示月份1~12
第5列标识号星期0~6(0表示星期天)
第6列要运行的命令

例子:

30 21 * * * /usr/local/etc/rc.d/lighttpd restart
上面的例子表示每晚的21:30重启apache。

45 4 1,10,22 * * /usr/local/etc/rc.d/lighttpd restart
上面的例子表示每月1、10、22日的4 : 45重启apache。

10 1 * * 6,0 /usr/local/etc/rc.d/lighttpd restart
上面的例子表示每周六、周日的1 : 10重启apache。

0,30 18-23 * * * /usr/local/etc/rc.d/lighttpd restart
上面的例子表示在每天18 : 00至23 : 00之间每隔30分钟重启apache。

0 23 * * 6 /usr/local/etc/rc.d/lighttpd restart
上面的例子表示每星期六的11 : 00 pm重启apache。

* */1 * * * /usr/local/etc/rc.d/lighttpd restart
每一小时重启apache

* 23-7/1 * * * /usr/local/etc/rc.d/lighttpd restart
晚上11点到早上7点之间,每隔一小时重启apache

0 11 4 * mon-wed /usr/local/etc/rc.d/lighttpd restart
每月的4号与每周一到周三的11点重启apache

0 4 1 jan * /usr/local/etc/rc.d/lighttpd restart
一月一号的4点重启apache

*/30 * * * * /usr/sbin/ntpdate 210.72.145.44
每半小时同步一下时间[]

补充:
大部分在 crontab 计划任务中都会年到未尾带 >/dev/null 2>&1,是什么意思呢?
> 是重定向
/dev/null 代表空设备文件
1 表示stdout标准输出,系统默认值是1,所以 “>/dev/null” 等同于 “1>/dev/null”
2 表示stderr标准错误
& 表示等同于的意思,2>&1,表示2的输出重定向等同于1

整句的意思就是标准输出重定向到空设备文件,也就是不输出任何信息到终端,标准错误输出重定向等同于标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件

备注:
在自己的开发机上运行成功,但是在docker上还有问题

nginx的简单的代理

nginx配置过于强大,这里是简单的代理配置。
当A访问不了C时,而B与A,C都是同的,A就可以通过B访问C。
简单的配置如下:

server {
    listen 8018;

    location / {

        proxy_pass http://10.22.1.201:18070;
        proxy_set_header   Host    $host;
        proxy_set_header   X-Real-IP   $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;

    }   

}

这样把所有的访问本地ip:8018的访问都转到了 10.22.1.201:18070上了。好像很简单哦

附录:
location 语法规则: location [=|~|~*|^~] /uri/ { … }
= 开头表示精确匹配
^~ 开头表示uri以某个常规字符串开头,理解为匹配 url路径即可。nginx不对url做编码,因此请求为/static/20%/aa,可以被规则^~ /static/ /aa匹配到(注意是空格)。
~ 开头表示区分大小写的正则匹配
~* 开头表示不区分大小写的正则匹配
!~和!~*分别为区分大小写不匹配及不区分大小写不匹配 的正则
/ 通用匹配,任何请求都会匹配到。
多个location配置的情况下匹配顺序为(参考资料而来,还未实际验证,试试就知道了,不必拘泥,仅供参考):
首先匹配 =,其次匹配^~, 其次是按文件中顺序的正则匹配,最后是交给 / 通用匹配。当有匹配成功时候,停止匹配,按当前匹配规则处理请求。

[yii]sae-yii-with-smarty

这里介绍了sae环境安装smarty-view-renderer扩展,亲测有效
一普通环境安装smarty-view-renderer
Yii 拥有smarty的扩展,而且很好用。具体看连接:http://www.yiiframework.com/extension/smarty-view-renderer
安装这个扩展需要三步:
1, 将扩展放在: protected/extensions下
2, 将smarty的lib放在 protected/vendors/Smarty下(smarty3请在官网下载)
3, 添加config的配置

'viewRenderer'=&gt;array(
  'class'=&gt;'application.extensions.yiiext.renderers.smarty.ESmartyViewRenderer',
    'fileExtension' =&gt; '.tpl',
    //'pluginsDir' =&gt; 'application.smartyPlugins',
    //'configDir' =&gt; 'application.smartyConfig',
),

二 根据SAE进行配置
一般的Yii到这里就可以使用了,但是SAE没有写权限,smarty在编译时产生编译文件,所以要将编译文件写在memcache中,但这需要耗费sae的豆!
1,/protected/vendor/Smarty/目录下的Smarty.class.php文件:
防止调用touch,saemc会自动更新时间,不需要touch

public $compile_locking = false;

2, /protected/extensions/ESmartyViewRenderer.php
修改为

    $this-&gt;getSmarty()-&gt;setTemplateDir('saemc://templates_c/');
$compileDir = isset($this-&gt;config['compile_dir']) ?
					  $this-&gt;config['compile_dir'] :'saemc://templates_c/';

写入到sae的memcache

三 测试
最后在自己的IndexController测试下:

    public function actionIndex() {
        
         $data = array(&quot;name&quot; =&gt; &quot;willwcw@163.com&quot;);
         $this-&gt;render(&quot;index&quot;, $data);
        
}

Tpl文件

mail:{$name}
this is tpl

如果参数替换成功,表示模板生效。
最后:源码放在github上了,空的框架,没有其他任何配置
https://github.com/wangchunwei/sae-yii-with-smarty