页面: 1 2 3 4 5 6 7 8 9 10 ...84 85 86 下一页

热门

十二月
26
2011

PHP的Realpath Cache

2
作者:AirForce

前言

PHP的缓存有很多种,包括输出缓冲(ob系列函数),opcode缓存(APCeAccelerator,XCache等扩展实现),这些大家已经很熟悉了,接下来介绍一下一个不太被人注意的PHP缓存机制:realpath_cache。

介绍

require,require_once,include,include_once这四个语句(并非函数)大家经常会用到,如果用这类语句去包含文件(相对路径)的话,那么PHP会去include_path所指定的路径中去查找相关文件。一个应用中会存在大量的require_once语句调用,如果每次调用都去include_path中查找相应的文件,势必会对应用的性能产生负面影响。为了避免这种负面效应产生的影响,PHPER们会使用文件的绝对路径来包含所需的文件,这样就减少了查询include_path的次数。

其实,PHP自5.1.0起,就引入了RealpathCache。RealpathCache可以把PHP所用到文件的realpath进行缓存,以便PHP再使用这些文件的时候不需要再去include_path中查找,加快PHP的执行速度。

配置

realpath cache的配置项有两个,分别为realpath_cache_size和realpath_cache_ttl,可以在php.ini中进行修改:

 

01 ; Determines the size of the realpath cache to be used by PHP. This value should
02 ; be increased on systems where PHP opens many files to reflect the quantity of
03 ; the file operations performed.
04 ; http://php.net/realpath-cache-size
05 ;realpath_cache_size = 16k
06
07 ; Duration of time, in seconds for which to cache realpathinformation for a given
08 ; file or directory. For systems with rarely changing files, consider increasing this
09 ; value.
10 ; http://php.net/realpath-cache-ttl
11 ;realpath_cache_ttl = 120

其中realpath_cache_size指定了realpath cache的大小,默认为16k,如果你觉得这个容量太小,可以适当增加;realpath_cache_ttl指定了缓存的过期时间,默认为120秒,对于不经常修改的生产环境来说,这个数字可以调整的更大些。

 

问题

由于realpath会展开symlink(即软连接),所以如果你使用修改symlink目标这种方式发布应用的新版本的话,realpath cache会导致一些问题的出现:当你修改symlink使其指向一个新的release目录时候,由于realpath cache所缓存内容还没有过期,于是就会出现应用使用的还是旧的release,直到realpath cache所缓存内容过期失效为止(默认120秒),或者重启php-fpm。

看个例子:
基础环境:nginx + fastcgi + php-fpm
应用环境:/var/www/app是一个symlink,并做为document_root,在/var/www下存在version0.1,version0.2两个版本的release。初始情况下/var/www/app指向version0.1

 

1 lrwxr-xr-x    1 weizhifeng  staff    10 10 22 16:41 app -> version0.1
2 drwxr-xr-x    3 weizhifeng  staff   102 10 22 16:43 version0.1
3 drwxr-xr-x    3 weizhifeng  staff   102 10 22 16:43 version0.2

 

version0.1,version0.2内部各有一个hello.php

1 [weizhifeng@Jeremys-Mac www]$ cat version0.1/hello.php
2 <?php
3 echo 'in version0.1';
4 ?>
5
6 [weizhifeng@Jeremys-Mac www]$ cat version0.2/hello.php
7 <?php
8 echo 'in version0.2';
9 ?>

 

nginx配置文件片段:

01 location / {
02             root /var/www/app;   #app为symlink
03             index  index.php index.html index.htm;
04 }
05
06 location ~ \.php$ {
07             root /var/www/app; #app为symlink
08             fastcgi_pass   127.0.0.1:9000;
09             fastcgi_index  index.php;
10             include        fastcgi_params;
11 }

 

此时通过HTTP访问hello.php,得到的内容是’in version0.1′;修改/var/www/app,使其指向version0.2

1 [weizhifeng@Jeremys-Mac www]$ rm -f app && ln -s version0.2/ app

 

修改完成之后通过HTTP访问hello.php,得到的内容仍旧是”in version0.1″,可见是realpath cache在作祟了,此时你可以重启php-fpm或者等待120秒钟让realpath cache失效。

你可以使用clearstatcache来清除realpath cache,但是这个只对当前调用clearstatcache函数的PHP进程有效,而其他的PHP进程还是无效,由于PHP进程池(php-fpm生成,或者Apache在prefork模式下产生的N个httpd子进程)的存在,这个方法不是很适用。

参考:

http://php.net/manual/en/ini.core.php#ini.sect.performance

http://sixohthree.com/1517/php-and-the-realpath-cache

标签
十二月
26
2011

http://weizhifeng.net/2011/07/03/extension-writing-part-i-introduction-to-php-and-zend/

原文:http://devzone.zend.com/article/1021-Extension-Writing-Part-I-Introduction-to-PHP-and-Zend

介绍

如果你在读这篇入门文章,那么你可能对写PHP扩展有点兴趣。如果不是… 好吧,那么等我们写完这篇文章,你将会发现一个之前自己完全不知道,但是非常有趣的东西。

这篇入门文章假设你对PHP语言和以及PHP的编写语言C语言都有一定的熟悉。

让我们以“为什么你需要写一个PHP扩展”作为开始。

  • 因为PHP语言本身抽象程度有限,有一些库或者操作系统级别的调用,不能用PHP直接调用。
  • 你想给PHP添加一些与众不同的行为。
  • 你已经写了一些PHP代码,但是当运行的时候你知道它可以更快,更小,消耗的内存更少。
  • 你有一部分程序想出售,你可以把它写成扩展,这样程序是可以执行的,但是别人却无法看到源码。

这儿有很多完美的原因,但是要想创建一个扩展,你首先要需要明白什么是扩展。

什么是扩展?

如果你用过PHP,那么你就用过扩展。除了一些极少的特殊情况之外,PHP语言中的每个用户空间函数都是以组的形式分布在一个或多个扩展之中。这些函数中的大部分是位于标准扩展中的 – 总共超过400个。PHP源码中包含86个扩展,平均每个扩展中有30个函数。算一下,大概有2500个函数。如果这个不够用,PECL仓库还提供了超过100个其他扩展,或者还可以在互联网上找到更多的扩展。

「PHP除了扩展中的这些函数之外,剩下的是什么」我听到了你的疑问「扩展是什么?PHP的核心又是什么?」

PHP的核心是由两个独立的部分组成的。在最底层是Zend Engine (ZE)。ZE 负责把人类可以理解的脚本解析成机器可以理解的符号(token),然后在一个进程空间内执行这些符号。ZE还负责内存管理,变量作用域,以及函数调用的调度。另一部分是PHP。PHP负责与SAPI层(Server Application Programming Interface,经常被用来与Apache, IIS, CLI, CGI等host环境进行关联)的交互以及绑定。它也为safe_modeopen_basedir检查提供了一个统一的控制层,就像streams层把文件和网络I/O与用户空间函数(例如fopen()fread()fwrite())关联起来一样。

生命周期

当一个给定的SAPI启动后,以/usr/local/apache/bin/apachectl start的响应为例,PHP便以初始化它的核心子系统作为开始。随着SAPI启动程序的结束,PHP开始加载每个扩展的代码,然后调用它们的模块初始化(MINIT)程序。这就给每个扩展机会用来初始化内部变量,申请资源,注册资源处理器,并且用ZE注册自己的函数,这样如果一个脚本调用这些函数中的一个,ZE就知道执行哪些代码。

接下来,PHP会等待SAPI层的页面处理请求。在CGI或者CLI SAPI情况下,这个请求会立即发生并且只执行一次。在Apache, IIS, 或者其他成熟的web服务器SAPI中,请求处理会在远程用户发起请求的时候发生,并且会重复执行很多次,也可能是并发的。不管请求是怎么进来的,PHP以让ZE来建立脚本可以运行的环境作为开始,然后调用每个扩展的请求初始化RINIT)函数。RINIT给了扩展一个机会,让其可以建立指定的环境变量,分配请求指定的资源,或者执行其他任务例如审计。关于RINIT函数调用最典型的例子是在session扩展中,如果session.auto_start选项是开启的,RINIT会自动触发用户空间的session_start()函数并且预先填充$_SESSION变量。

当请求一旦被初始化,ZE便把PHP脚本翻译成符号(token),最终翻译成可以进行单步调试和执行的opcode。如果这些opcode中的一个需要调用一个扩展函数,ZE将会给那个函数绑定参数,并且临时放弃控制权直到函数执行完成。

当一个脚本完成了执行之后,PHP将会调用每个扩展的请求结束(RSHUTDOWN)函数来执行最后的清理工作(比如保存session变量到磁盘上)。接下来,ZE执行一个清理过程(熟知的垃圾回收),实际上是对上次请求过程中使用的变量调用unset()函数。

一旦完成,PHP等待SAPI发起另一个文档请求或者一个关闭信号。在CGI和CLI SAPI的情况下,没有所谓的“下一个请求”,所以SAPI会立刻执行关闭流程。在关闭过程中,PHP又让每个扩展调用自己的模块关闭MSHUTDOWN)函数,最后关闭自己的核心子系统。

这个过程第一次听令人有些费解,但是一旦你深入到一个扩展的开发过程中,它就会逐渐的清晰起来。

内存分配

为了避免写的很糟糕的扩展泄露内存,ZE以自己内部的方式来进行内存管理,通过用一个附加的标志来指明持久化。一个持久化分配的内存比单个页面请求存在的时间要长。一个非持久化分配的内存,相比之下,在请求结束的时候就会被释放,不管free函数是否被调用。例如用户空间变量,都是非持久化分配的内存,因为在请求结束之后这些变量都没有用了。

一个扩展理论上可以依靠ZE在每个页面请求结束后自动释放非持久化的内存,但这是不被推荐的。在请求结束的时候,分配的内存不会被立即被回收,并且会持续一段时间,所以和那块内存关联的资源将不会被恰当的关闭,这是一个很糟的做法,因为如果不能适当的清理的话,这会产生混乱。就像你即将要看见的,确定所有分配的数据被恰当的清除了是非常的简单。

让我们把常规的内存分配函数(只应该当和内部库一起工作的时候才会用到)和PHP ZE中的持久化和非持久化内存分配函数进行一个对比。

Traditional Non-Persistent Persistent
malloc(count)
calloc(count, num)
emalloc(count)
ecalloc(count, num)
pemalloc(count, 1)*
pecalloc(count, num, 1)
strdup(str)
strndup(str, len)
estrdup(str)
estrndup(str, len)
pestrdup(str, 1)
pemalloc() & memcpy()
free(ptr) efree(ptr) pefree(ptr, 1)
realloc(ptr, newsize) erealloc(ptr, newsize) perealloc(ptr, newsize, 1)
malloc(count * num + extr)** safe_emalloc(count, num, extr) safe_pemalloc(count, num, extr)
The pemalloc() family include a ‘persistent’ flag which allows them to behave like their non-persistent counterparts.
For example: emalloc(1234) is the same as pemalloc(1234, 0)

** safe_emalloc() and (in PHP 5) safe_pemalloc() perform an additional check to avoid integer overflows

建立一个开发环境

现在你已经掌握了一些关于PHP和ZE的工作原理,我估计你希望要深入进去,并且开始写些什么。无论如何在你能做之前,你需要收集一些必要的开发工具,并且建立一个满足自己目标的环境。

第一你需要PHP本身,以及构建PHP所需要的开发工具集合。如果你对于从源码编译PHP不熟悉,我建议你看看http://www.php.net/install.unix。(开发windows下的PHP扩展在以后的文章会介绍)。使用适合自己发行版的PHP二进制包是很诱人的,但是这些版本总是会忽略两个重要的

./configure

选项,这两个选项在开发过程中非常方便。第一个是--enable-debug。这个选项将会用附加符号信息来编译PHP所以,如果一个段错误发生,那么你将可以从PHP收集到一个核心dump信息,然后使用gdb来跟踪这个段错误是在哪里发生的,为什么会发生。另一个选项依赖于你将要进行扩展开发的PHP版本。在PHP4.3这个选项叫--enable-experimental-zts,在PHP5和以后的版本中叫--enable-maintainer-zts。这个选项将会让PHP思考在多线程环境中的行为,并且可以让你捕获常见的程序错误,这些错误在非线程环境中不会引起问题,但在多线程环境中却使你的扩展变得不可用。一旦你已经使用这些额外的选项编译好了PHP,并且已经安装在了你的开发服务器(或者工作站)上,那么你可以开始建立你的第一个扩展了。

Hello World

如果一门语言的入门介绍没有Hello World程序,那么这个介绍就是不完整的。在这种情况下,你将会建立一个扩展,这个扩展会导出一个返回”Hello World”字符串的函数。如果用PHP,你可能这么写:

 

1 <?php
2 function hello_world()
3 {    
4     return 'Hello World';
5 }
6 ?>

 

现在你将会把这个逻辑放到一个PHP扩展中。首先让我们在你PHP源码树的ext/目录下创建一个名叫hello的目录,并进入(chdir)到这个目录中。这个目录实际上可以放在任何地方,PHP源码树内或者PHP源码树外,但是我希望你把它放在源码树内为了接下来的文章使用。在这你需要创建三个文件:一个包含你hello_world函数的源文件,一个头文件,其中包含PHP加载你扩展时候所需的引用,一个配置文件,它会被phpize用来准备扩展的编译环境。
config.m4

 

PHP_ARG_ENABLE(hello, whether to enable Hello World support,
[ --enable-hello Enable Hello World support])</code>

if test "$PHP_HELLO" = "yes"; then
    AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
    PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
fi

php_hello.h

 

 

#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1

#define PHP_HELLO_WORLD_VERSION "1.0"
#define PHP_HELLO_WORLD_EXTNAME "hello"

PHP_FUNCTION(hello_world);

extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry

#endif

hello.c

 

 

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_hello.h"

static function_entry hello_functions[] = {
    PHP_FE(hello_world, NULL)
    {NULL, NULL, NULL}
};

zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_HELLO_WORLD_EXTNAME,
    hello_functions,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_HELLO_WORLD_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif

PHP_FUNCTION(hello_world)
{
    RETURN_STRING("Hello World", 1);
}

以上只是一个PHP扩展的大体框架,扩展中的大部分代码只是简单的把几个文件关联在了一起。只有最后四句才像你之前在PHP脚本中调用的“实际代码”。实际上这个层级的代码和我们之前看到的PHP代码非常的相似,从字面上很容易理解:
1. 声明一个名叫hello_world的函数
2. 让那个函数返回一个字符串:“Hello World”
3. ….额…. 1? 那个1是做什么的?

 

回想一下ZE有一个先进的内存管理层,当脚本退出的时候确保分配的资源被释放掉。在内存管理领域,对同一块内存进行两次释放是大错特错的。这种做法叫做double freeing,是引起段错误的常见原因,因为它让程序去访问一个已经不属于自己的内存块。类似的,你不希望让ZE去释放一个静态字符串buffer(就像我们示例扩展中的”Hello World”),因为它是在程序空间,并不是属于任何进程的数据块。RETURN_STRING()假设任何传递给它的字符串都需要一个拷贝,所以它们可以在之后安全的释放掉。但是由于在一个内部函数中为字符串分配内存,动态填充,然后返回它,这是很平常,RETURN_STRING()允许我们来指定是否有必要对这个字符串值进行拷贝。为了更好的解释这个概念,接下来的代码片段的功能和上面的是一样的:

 

PHP_FUNCTION(hello_world)
{
    char *str;

    str = estrdup("Hello World");
    RETURN_STRING(str, 0);
}

 

在这个版本中,你手动为”Hello World”字符串分配了内存,最终返回给调用脚本,然后把内存“给了”RETURN_STRING,第二个参数值0说明不需要为这个字符串做拷贝。

建立你的扩展

这个练习的最后一步是把你的扩展编译成一个动态加载的模块。如果你已经正确的拷贝以上的例子,那么这个工作就是在ext/hello/目录下执行三个命令:

 

1 $ phpize
2 $ ./configure --enable-hello
3 make

 

在运行了这些命令之后,你将会在ext/hello/modules目录中发现一个hello.so文件。现在,可以像其他PHP扩展一样,你可以把它拷贝到你的扩展目录(默认是/usr/local/lib/php/extensions/,检查你的php.ini文件确定一下)中,然后在你的php.ini文件中加上extension=hello.so这一行,让扩展可以在PHP启动的时候被加载。对于CGI/CLI SAPI来说,这个意味着下一次PHP运行的时候就会生效;对于web server SAPI比如Apache来说,这个意味着web server下次被重启的时候生效。现在让我们以命令行的形式做一个尝试:

 

1 $ php -r 'echo hello_world();'

 

如果一切正常,你将会看到由这个脚本输出的Hello World,因为在你加载的扩展中已经定义的hello_world()函数会返回Hello World这个字符串,然后echo命令会打印出任何传递给它的东西。

其他标量也可以用类似的函数返回,用RETURN_LONG()返回整型值,RETURN_DOUBLE()返回浮点型值,RETURN_BOOL()返回布尔型值,RETURN_NULL()返回的值,你懂的,NULL。在hello.c文件中的function_entry结构体中加入几行PHP_FE()代码并且在文件最后加入几行PHP_FUNCTION()代码,让我们真实的看看这些函数。

 

static function_entry hello_functions[] = {
    PHP_FE(hello_world, NULL)
    PHP_FE(hello_long, NULL)
    PHP_FE(hello_double, NULL)
    PHP_FE(hello_bool, NULL)
    PHP_FE(hello_null, NULL)
    {NULL, NULL, NULL}
};

PHP_FUNCTION(hello_long)
{
    RETURN_LONG(42);
}

PHP_FUNCTION(hello_double)
{
    RETURN_DOUBLE(3.1415926535);
}

PHP_FUNCTION(hello_bool)
{
    RETURN_BOOL(1);
}

PHP_FUNCTION(hello_null)
{
    RETURN_NULL();
}

 

你还需要在头文件php_hello.h中为这些函数添加原型声明,添加在hello_world()函数原型旁边,这样构建程序就可以恰当的进行宏替换:

PHP_FUNCTION(hello_world);
PHP_FUNCTION(hello_long);
PHP_FUNCTION(hello_double);
PHP_FUNCTION(hello_bool);
PHP_FUNCTION(hello_null);

如果你对config.m4文件没有做过更改,那么这次跳过phpize./configure步骤,直接make,在技术上来说这是安全的。但是无论如何,为了能够没有问题的构建这个扩展,这次我还是想让你完整的走这三个步骤。另外,这次你应该用make clean,而不是上次用的make,从而确保所有源文件都被重新构建。其实这个还是不必要的,因为你做的修改很有限,但是安全比混乱要好。一旦模块构建好了之后,你可以把它拷贝到你的扩展目录下,替换旧的版本。

 

此时你可以再一次调用PHP解释器,用一个简单的脚本来测试你刚才加的函数。事实上,为什么你现在不做呢?我在这儿等你….

测试好了?很好。如果你使用var_dump()而不是echo来看每个函数的输出的话,你可能会注意到hello_bool()返回的是true。这是RETURN_BOOL()函数中1所代表的值。就像在PHP脚本中,一个整型的0等于FALSE,同时任何其他的整型值等于TRUE。扩展的作者们经常使用1来表示TRUE,也建议你那样做,但是不要拘泥于此。为了添加可读性,RETURN_TRUE
RETURN_FALSE宏也是可用的;下面是hello_bool()的重写,这次使用RETURN_TRUE

 

PHP_FUNCTION(hello_bool)
{
    RETURN_TRUE;
}

 

注意这没有使用括号。RETURN_TRUERETURN_FALSE跟其他RETURN_*()宏不一样,所以别搞错了。

你可能注意到在以上代码示例中,我们没有传递0或者1来指定是否这个值需要被拷贝。这是因为对于这些简单的标量来说,并没有额外的内存被分配或者释放。

这还有三个额外的返回类型:RESOURCEmysql_connect(),fsockopen()ftp_connect()等函数返回的类型),ARRAY(也就是HASH表),OBJECTnew关键字返回的)。这些类型我们将会在第二部分也就是深入变量的时候来介绍。

INI设置

Zend引擎提供了两种管理INI值的方法。我们现在先看一下简单的方法,之后当你有机会使用全局变量的时候,再看一下更加完整,更加复杂的方法。

假设你想在你的扩展中声明一个php.ini的配置项,hello.greeting,这个值被你的函数hello_world()所使用。你需要对hello_module_entry做些关键的修改,同时还需要在hello.cphp_hello.h中添加些东西。在php_hello.h的用户区函数原型附近添加如下的函数原型:

 

PHP_MINIT_FUNCTION(hello);
PHP_MSHUTDOWN_FUNCTION(hello);

PHP_FUNCTION(hello_world);
PHP_FUNCTION(hello_long);
PHP_FUNCTION(hello_double);
PHP_FUNCTION(hello_bool);
PHP_FUNCTION(hello_null);

 

现在到hello.c文件顶部,用以下内容替换掉hello_module_entry的内容:

 

zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_HELLO_WORLD_EXTNAME,
    hello_functions,
    PHP_MINIT(hello),
    PHP_MSHUTDOWN(hello),
    NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_HELLO_WORLD_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

PHP_INI_BEGIN()
PHP_INI_ENTRY("hello.greeting", "Hello World", PHP_INI_ALL, NULL)
PHP_INI_END()

PHP_MINIT_FUNCTION(hello)
{
    REGISTER_INI_ENTRIES();

    return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(hello)
{
    UNREGISTER_INI_ENTRIES();

    return SUCCESS;
}

 

现在,你只需要在hello.c文件头部的#inlcude代码后面添加一行,从获取对INI文件支持所需要的正确头文件:

 

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "php_hello.h"

 

最后,你可以修改你的hello_world函数来使用INI值:

 

PHP_FUNCTION(hello_world)
{
    RETURN_STRING(INI_STR("hello.greeting"), 1);
}

 

注意,你拷贝了从INI_STR()返回的值。因为这是一个静态的字符串。事实上,如果你尝试去修改INI_STR返回的这个字符串,PHP执行环境将会变得不稳定,甚至会崩溃。

首先要修改的地方是你非常熟悉的两个函数:MINITMSHUTDOWN。就像前面提到的,这些函数会在SAPI层初始化启动和最后关闭的时候被调用。他们不会在请求过程中被调用。在这个例子中,你已经用这些函数在你的扩展中注册了php.ini的配置内容。在接下来的内容中,你将会知道如何用MINITMSHUTDOWN函数来注册resource,object和stream handler。

在你的hello_world()函数中,你用INI_STR()来获得了hello.greeting当前的值,字符串格式。在下面表格中列出了一些其他函数,这些函数可以返回long,double和Boolean类型的值,并且还有一些带有ORIG标识的更加原始的函数,这些函数返回php.ini中最初设置的值(在被.htaccess文件或者ini_set()修改之前)。

Current Value Original Value Type
INI_STR(name) INI_ORIG_STR(name) char * (NULL terminated)
INI_INT(name) INI_ORIG_INT(name) signed long
INI_FLT(name) INI_ORIG_FLT(name) signed double
INI_BOOL(name) INI_ORIG_BOOL(name) zend_bool

传递给PHP_INI_ENTRY()的第一个参数是在php.ini中使用的配置项名称。为了避免命名空间的冲突,你应该使用跟你函数命名相同的习惯;在所有的配置项之前都加一个和你扩展名字相同的前缀,就像hello.greeting一样。事实上习惯就是,一个“.”把扩展名字和ini配置的名字分开。

第二个参数是初始化值,不管它是否是数字类型的,总是传递char*字符串类型。这是因为事实上.ini文件中的值都是原生的文本类型。你可以在你的脚本中用INI_INT()INI_FLT(),或者INI_BOOL()来做类型转换。

你传递的第三个值是一个访问模式标识。这是一个掩码字段,用来决定在什么时候,在什么地方这个INI的配置项可以被修改。一些配置项,比如像register_globals,它就不可能在脚本中用ini_set()来进行修改,因为这个配置项只有在请求启动的时候才有意义,也就是脚本根本就没有机会去修改它。其他的,比如像allow_url_fopen,它是管理员级别的配置项,所以你不希望在共享托管环境中的用户去修改它,不管是通过ini_set()还是用.htaccess指令。这个参数常见的值可能是PHP_INI_ALL,表明这个配置项可以在任何地方修改。还有PHP_INI_SYSTEM|PHP_INI_PERDIR,表明配置项可以在php.ini文件或者在.htaccess文件通过Apache的指令来修改,但是不能使用ini_set()来修改。PHP_INI_SYSTEM,表示这个配置项只能在php.ini中修改,不能在其他地方修改。

当前我们将要跳过第四个参数,只是提一下这个参数允许传递一个回调方法,这个方法会在ini配置被修改的时候触发,无论什么时候,比如用ini_set()修改。这就允许一个扩展可以在配置被修改的时候做一些更准确的控制,或者触发一个需要依赖新配置的动作。

全局变量

通常,一个扩展在一个特殊的请求中需要跟踪一个值,并保证这个值与同一时间其他的请求是独立开来的。在一个非线程SAPI中那很简单:在源文件中直接声明一个全局变量,在需要的时候访问它。麻烦是,自从PHP被设计成可以运行在多线程的web服务器上(像Apache2和IIS),所以需要把一个线程使用的全局变量与其他线程使用的全局变量分离开来。PHP用TSRM (Thread Safe Resource Management)抽象层,有时有也叫ZTS (Zend Thread Safety),非常简单的解决了这个问题。

事实上,你已经用过了TSRM的一部分,只是不知道而已。(先别费劲搜索呢;你将会发现这些东西都被隐藏了。)

创建一个线程安全的全局变量的第一步,和其他全局变量都一样,先声明。由于这个例子的缘故,你必须声明一个long类型值为0的全局变量。每次调用hello_long()函数的时候,你将会增加这个值,然后返回它。在php_hello.h中的#define PHP_HELLO_H代码段后面加上以下的代码:

#ifdef ZTS
#include "TSRM.h"
#endif

ZEND_BEGIN_MODULE_GLOBALS(hello)
    long counter;
ZEND_END_MODULE_GLOBALS(hello)

#ifdef ZTS
#define HELLO_G(v) TSRMG(hello_globals_id, zend_hello_globals *, v)
#else
#define HELLO_G(v) (hello_globals.v)
#endif

这次你还是要使用RINIT方法,所以你需要在头文件中声明它的原型:

PHP_MINIT_FUNCTION(hello);
PHP_MSHUTDOWN_FUNCTION(hello);
PHP_RINIT_FUNCTION(hello);

现在让我们回到hello.c中,在你的include块后面加上如下内容:

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "php_hello.h"

ZEND_DECLARE_MODULE_GLOBALS(hello)

 

修改hello_module_entry,添加PHP_RINIT(hello):

zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_HELLO_WORLD_EXTNAME,
    hello_functions,
    PHP_MINIT(hello),
    PHP_MSHUTDOWN(hello),
    PHP_RINIT(hello),
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_HELLO_WORLD_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

并修改你的MINIT函数,和另一对函数一起,用来在请求开始的时候初始化:

static void php_hello_init_globals(zend_hello_globals *hello_globals)
{
}

PHP_RINIT_FUNCTION(hello)
{
    HELLO_G(counter) = 0;

    return SUCCESS;
}

PHP_MINIT_FUNCTION(hello)
{
    ZEND_INIT_MODULE_GLOBALS(hello, php_hello_init_globals, NULL);

    REGISTER_INI_ENTRIES();

    return SUCCESS;
}

最后,你可以修改hello_long()函数来使用这个值:

PHP_FUNCTION(hello_long)
{
    HELLO_G(counter)++;

    RETURN_LONG(HELLO_G(counter));
}

php_hello.h添加的内容中,你使用了一对宏ZEND_BEGIN_MODULE_GLOBALS()ZEND_END_MODULE_GLOBALS() – 用来创建一个包含一个long类型,名为zend_hello_globals的结构体。然后你继续声明了HELLO_G()来从一个线程池中获取值,或者只是从全局空间中获取 - 如果你为一个非线程环境编译的话。

 

hello.c中你用了ZEND_DECLARE_MODULE_GLOBALS()宏来真正实例化zend_hello_globals结构体为一个真正的全局变量(如果是以非线程安全编译的话),或者一个线程资源池的一个成员。对于一个扩展的作者来说,这个区别我们不需要担心,因为Zend Engine已经为我们处理了这个事情。最后,在MINIT中,你使用了ZEND_INIT_MODULE_GLOBALS()来分配一个线程安全的资源id – 现在不用担心这个东西是什么。

你可能注意到了那个php_hello_init_globals()函数实际上根本没做任何事情,我们想在其中初始化counter0,而实际上我们是在RINIT中初始化的。为什么?

关键在于这两个函数什么时候被调用。php_hello_init_globals()只有当一个新的进程或者线程启动的时候才会被调用;而与此同时,每个进程可以处理多个请求,所以用这个函数来初始化我们的counter0的话,那么这个初始化只会在第一个页面请求到达的时候工作。等随后到达这个相同进程的页面请求,得到的仍然是旧的counter值,因此也就不会从0开始计数了。为了让每个单独的页面请求都能初始化counter0,我们实现了RINIT函数,就像你之前了解的那样,这个函数在每次页面请求的时候都会被调用。我们在这个时候包含了php_hello_init_globals()函数是因为你将会在一段时间后使用它,同时也是由于如果把一个NULL做为初始化函数传递给ZEND_INIT_MODULE_GLOBALS()将会在非线程平台上引起一个段错误。

INI配置项作为全局变量值

如果你回想起之前,一个用PHP_INI_ENTRY()声明的php.ini的配置项被解析成一个字符串值,并且在需要的时候可以用INI_INT()INI_FLT()INI_BOOL()转换成对应的类型。

对于一些配置项,存在很多不必要的重复工作,比如配置项的值在一个脚本执行的时候被一遍又一遍的读取。幸运的是可以让ZE以一种特殊的数据类型来存储INI配置项的值,并且只有值改变的时候才执行类型转换。让我们声明另一个INI配置的值,这次是一个Boolean类型,用来标示counter是否增加或者减少。修改php_hello.h文件的MODULE_GLOBALS块为以下内容:

ZEND_BEGIN_MODULE_GLOBALS(hello)
    long counter;
    zend_bool direction;
ZEND_ENG_MODULE_GLOBALS(hello)

接下来,修改PHP_INI_BEGIN()块内容从而来声明INI配置项的值:

PHP_INI_BEGIN()
    PHP_INI_ENTRY("hello.greeting", "Hello World", PHP_INI_ALL, NULL)
    STD_PHP_INI_ENTRY("hello.direction", "1", PHP_INI_ALL, OnUpdateBool, direction, zend_hello_globals, hello_globals)
PHP_INI_END()

现在,在init_globals方法中初始化配置项:

static void php_hello_init_globals(zend_hello_globals *hello_globals)
{
    hello_globals->direction = 1;
}

最后,在hello_long()函数中使用INI配置项的值来决定是否要增加或者减少counter

PHP_FUNCTION(hello_long)
{
    if (HELLO_G(direction)) {
        HELLO_G(counter)++;
    } else {
        HELLO_G(counter)--;
    }

    RETURN_LONG(HELLO_G(counter));
}

这就是全部了。在INI_ENTRY中指定的OnUpdateBool方法将会自动的转换php.ini.htaccess文件提供的或者在脚本中通过ini_set()设置的值称为TRUE或者FALSE。STD_PHP_INI_ENTRY的最后三个参数是来告诉PHP修改哪个全局变量,我们扩展的全局变量的数据结构,以及这些全局变量被保存到的全局容器的名称。

 

稳妥的检查

到现在我们的三个文件看起来应该像下面所列的一样。(一些内容已经被移除了,并且规整到一起,只为了易读)
config.m4

PHP_ARG_ENABLE(hello, whether to enable Hello World support,
[ --enable-hello Enable Hello World support])

if test "$PHP_HELLO" = "yes"; then
    AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
    PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
fi

 

php_hello.h

#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1

#ifdef ZTS
#include "TSRM.h"
#endif

ZEND_BEGIN_MODULE_GLOBALS(hello)
    long counter;
    zend_bool direction;
ZEND_END_MODULE_GLOBALS(hello)

#ifdef ZTS
#define HELLO_G(v) TSRMG(hello_globals_id, zend_hello_globals *, v)
#else
#define HELLO_G(v) (hello_globals.v)
#endif

#define PHP_HELLO_WORLD_VERSION "1.0"
#define PHP_HELLO_WORLD_EXTNAME "hello"

PHP_MINIT_FUNCTION(hello);
PHP_MSHUTDOWN_FUNCTION(hello);
PHP_RINIT_FUNCTION(hello);

PHP_FUNCTION(hello_world);
PHP_FUNCTION(hello_long);
PHP_FUNCTION(hello_double);
PHP_FUNCTION(hello_bool);
PHP_FUNCTION(hello_null);

extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry

#endif

 

hello.c

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "php_hello.h"

ZEND_DECLARE_MODULE_GLOBALS(hello)

static function_entry hello_functions[] = {
    PHP_FE(hello_world, NULL)
    PHP_FE(hello_long, NULL)
    PHP_FE(hello_double, NULL)
    PHP_FE(hello_bool, NULL)
    PHP_FE(hello_null, NULL)
    {NULL, NULL, NULL}
};

zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
   PHP_HELLO_WORLD_EXTNAME,
   hello_functions,
   PHP_MINIT(hello),
   PHP_MSHUTDOWN(hello),
   PHP_RINIT(hello),
   NULL,
   NULL,
#if ZEND_MODULE_API_NO >= 20010901
   PHP_HELLO_WORLD_VERSION,
#endif
   STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif

PHP_INI_BEGIN()
    PHP_INI_ENTRY("hello.greeting", "Hello World", PHP_INI_ALL, NULL)
STD_PHP_INI_ENTRY("hello.direction", "1", PHP_INI_ALL, OnUpdateBool, direction, zend_hello_globals, hello_globals)
PHP_INI_END()

static void php_hello_init_globals(zend_hello_globals *hello_globals)
{
    hello_globals-&gt;direction = 1;
}

PHP_RINIT_FUNCTION(hello)
{
    HELLO_G(counter) = 0;

    return SUCCESS;
}

PHP_MINIT_FUNCTION(hello)
{
    ZEND_INIT_MODULE_GLOBALS(hello, php_hello_init_globals, NULL);

    REGISTER_INI_ENTRIES();

    return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(hello)
{
    UNREGISTER_INI_ENTRIES();

    return SUCCESS;
}

PHP_FUNCTION(hello_world)
{
    RETURN_STRING("Hello World", 1);
}

PHP_FUNCTION(hello_long)
{
    if (HELLO_G(direction)) {
        HELLO_G(counter)++;
    } else {
        HELLO_G(counter)--;
    }

    RETURN_LONG(HELLO_G(counter));
}

PHP_FUNCTION(hello_double)
{
    RETURN_DOUBLE(3.1415926535);
}

PHP_FUNCTION(hello_bool)
{
    RETURN_BOOL(1);
}

PHP_FUNCTION(hello_null)
{
    RETURN_NULL();
}

 

接下来是什么?

在这个教程中,我们探寻了一个简单PHP扩展的结构,这个扩展向用户空间增加了函数,返回了值,声明了INI配置,跟踪了一个请求过程中的内部状态。

在下一个话题中,我们将要探寻PHP变量的内部结构,看看它们在一个脚本环境中是什么怎么样被存储,跟踪,以及维护的。当一个函数被调用时候,我们将要使用zend_parse_parameters来接收参数,然后探寻如何返回更复杂的结果,包括这次教程中所提及的数组对象,以及资源类型。

标签
十二月
19
2011

韩国国防研究院最近发布的一份国防报告显示,朝鲜的实际国防费,比朝鲜当局公布的国防费,要多出13倍至15倍。这份报告显示,虽然从1990年代以来,朝鲜经历严重的经济危机、经济一直呈现负成长,但国防费投入有增无减,甚至持续增加。分析指出,朝鲜增加的国防费,主要用在增强陆军快速机动和作战能力,改进远程飞弹能力,持续提高大规模杀伤性武器的能力。

由此看去,朝鲜似乎是非常的强悍。

但韩国外交安全首席秘书官千英宇2011年1月14日接受美国电视台PBS采访时谈到韩国政府的判断说,“朝鲜挺不了多久。”有分析认为,目前韩国政府对朝鲜采取强硬态度,是根据这一观点的。PBS也评论说,韩国政府认为朝鲜可能会陷入崩溃的边缘,制定对朝政策时将受到其影响。据韩国政府的内部资料,在前任金大中、卢武铉总统执政10年来从韩国流入朝鲜的实物和现金总额达69.5950亿美元(年均7亿美元),其中包括韩国援助的粮食、化肥以及经济合作等资金(约29亿美元)。这个数额等于同期中国的对朝援助总额19亿美元的3.7倍,等于朝鲜出口总额77亿美元的90%。韩国政府负责人说,“朝鲜对中国贸易逆差为年均达7至8亿美元,朝鲜是靠韩国的援助及贸易收入来弥补这一逆差的。”2010年朝鲜对中国的出口为约10亿美元,进口为约18亿美元。

但李明博执政3年以来,朝鲜没有得到韩国的粮食和化肥援助。这与过去10年来从韩国得到270万吨粮食和256万吨化肥形成鲜明的对比。这些物资价值达32亿美元。其间,朝鲜还通过出口水产品、沙子等贸易获得年均3亿美元左右的收入,但2010年发生天安舰事件以后全部停止了。现在,朝鲜剩下的只有来自开城工业园区的年工资收入5000万美元。就是说,朝鲜从韩国获得的实物和现金从年均7亿美元减少到了5000万美元。

随着朝鲜加强核武装并连续进行挑衅,国际社会的对朝援助也大幅减少。美国自由亚洲电台2010年12月援引联合国人道主义协调办公室相关人士的话报道说,2010年国际社会对朝鲜的人道主义援助资金约为2060万美元,这等于2009年5875万美元的35%。现在,朝鲜内部的经济情况很差。国情院下属国家安全战略研究所在《2011年朝鲜展望》报告中说,今年朝鲜的粮食产量估计为380万至390万吨,比2010年将减少20万至30万吨。朝鲜的粮食情况直接影响民心。政府高层人士说,2010年朝鲜部分打开军粮库(2号库)勉强度过了青黄不接时期。但有观测认为,今年没有那样的余地。除了经济困难,金正日的健康、韩国电视剧等资本主义风潮的扩散等也在削弱朝鲜体制的维持能力。

此前,朝鲜一直声称的2012年建设“强盛大国”的目标,现在却突然改为2020年,并说要达到发达国家水平。有观测认为,这是朝鲜自认截止明年克服经济困难不可能,并修订了目标。朝鲜朝鲜中央通讯社在1月15日报道“国家经济开发十年战略计划”说,“该计划展现了2012年打造跨入强盛大国之门的基础,到2020年达到发达国家水平的明确的前景”。

朝鲜的“强盛大国”提法,是金正日正式上台的1998年开始使用,要到2012年建成的目标,是2007年11月30日举行的全国知识分子大会上决定的。此后朝鲜一直沿用了“2012年建成强盛大国”这一提法。

对此,高丽大学教授柳浩烈认为,“朝鲜自2007年11月以来,一直惯用到金日成诞生100周年的2012年‘一定要打开走向强盛大国之门’的提法。相比之下,‘打造跨入强盛大国之门的基础’的提法显然令人感到把目标向下调低。”韩国统一部人士表示,“这表明朝鲜自己也认为2012年建成强盛大国很难,就偷偷地插进了‘到2020年达到发达国家水平’的话。但又不能把已经说惯了的‘2012年建设强盛大国’的提法拿掉,于是搞文字游戏保留了其痕迹。”当天朝鲜中央通讯社还说:“内阁把属于国家经济开发战略计划的主要项目全权委托给朝鲜大丰国际投资集团负责实施。”大丰集团是2009年12月朝鲜为引进外资依靠朝鲜族企业家朴哲洙成立的公司。但与大张旗鼓的开张相反,因国际社会的对朝经济制裁,引进外资至今几乎没有任何业绩。

日本《读卖新闻》曾报道,朝鲜王储金正恩提出:““应该在3年内让居民吃上米饭肉汤。”《读卖新闻》援引朝中关系消息灵通人士的话报道说,2010年11月上旬金正恩在平壤开会时说,“国民经济要在3年内恢复到上世纪60年代到70年代的水平,达到吃米饭、喝肉汤、住瓦房、穿绸缎的生活水平。”据悉,会议由金正恩的姑父、劳动党行政部长张成泽主持,出席会议的有企业家和经济专家等。“米饭肉汤”曾是金日成提出的口号。《读卖新闻》分析认为,金正恩是要借助祖父头上的光辉在经济建设领域占据领导地位。《读卖新闻》报道说,金正恩在确定王储地位的9月之后经常发表重视经济的言论,如“过去可以没有粮食,但不能没有子弹,如今是可以没有子弹,但一定要有粮食”。

然而现在看来,朝鲜做了几十年的 “米饭肉汤”美梦,也要做不成了。2011年1月1日出版的《今日朝鲜》在一篇歌颂金正日的文章中称:在经过半个世纪精力充沛地进行领导的日子里,他有时睡在车座上,用菜团子充饥。金正日跟干部们说,论睡觉打盹好,论吃饭,菜团子最好吃。这句话就如实地说明他怀着对祖国、革命、人民的无限热爱,废寝忘食地、精力充沛地进行活动。国家最高领袖都没有“米饭肉汤”,只得以菜团子充饥!

不过,美国《时代》周刊曾透露过有关金正日在饮食生活上鲜为人知的一面,到维吾尔去找哈密瓜以及葡萄,也到马来西亚、泰国去找热带水果如榴连、芒果、木瓜等,到捷克去找生啤酒,丹麦则是去买猪肉,去伊朗以及乌兹别克买鱼子酱,然后到日本买鱼,也曾从澳门带龙眼给金正日。金正日对嗜好品非常挑剔。金正日喝水只喝法国产的碳酸水沛绿雅,喝酒只喝法国的马爹利,抽烟则只抽薄荷香的进口烟。金正日到底过的是贵族生活,还是“用菜团子充饥”,朝鲜人民还不是被蒙在鼓里。他想怎么说就怎么说,说得比唱得还好听。

尽管最高领袖在“用菜团子充饥”,但对于王储的安乐窝却舍得下本钱,12月1日英国电讯报就报道说朝鲜投入1亿英镑以上在平壤和咸镜朝道温泉地区等为金正恩建豪华官邸。

1月8日,是朝鲜王储金正恩的生日,但朝鲜没有大肆庆祝。就连朝鲜广播也没有报道,也没有举行百姓们的庆祝活动。金正恩是朝鲜向国内外宣布的王储,而如此低调过他的生日实属罕见。一些朝鲜居民说:“起码要有业绩才能举行生日宴会啊。”金正恩从2009年开始崭露头角。朝鲜在2009年后推进了三大计划,分别是150天战斗、在平壤新建10万套住房和货币改革。始于2009年春天的150天战斗是一种提高产量的运动,但从大米产量的情况看,朝鲜仅实现目标的一半。在平壤新建10万套住房的计划截止到2012年,2010年要建设3.5万套,但由于水泥不足,与目标相差甚远。

朝鲜在2009年11月30日宣布进行货币改革,而到一年后的今天,大米价格上涨了75倍。目前在朝鲜市场居民用美元和人民币进行交易。朝鲜货币几乎丧失了作为货币的功能。

朝鲜政权唯一能炫耀的只有对南挑衅。朝鲜在2009年7月7日成功发动了分布式拒绝服务攻击。2010年3月炸沉天安舰,同年11月又对延坪岛发动了炮击。从朝鲜政权的立场看,这些都是“成果”,但又不能向居民公开宣传这些成果都是金正恩业绩,所以陷入两难境地。朝鲜主张网络攻击和天安舰事件不是自己所为。因此,如果公开宣传这两件事是金正恩主导策划的,那么谎言就会不攻自破。此外,朝鲜也不敢公开宣传延坪岛炮击事件是金正恩的业绩。因为国际刑事法庭(ICC)已着手调查朝鲜有没有触犯战争罪行。据悉,延坪岛炮击事件后在朝鲜军队内部曾短暂流传“延坪岛攻击是金正恩主导策划”的说法,但在ICC宣布要对延坪岛炮击事件进行调查后,这种说法立刻消失无踪。如果朝鲜向居民公开宣传延坪岛炮击是金正恩的业绩,将为ICC 把金正恩当作战争犯起诉提供依据。

因此,朝鲜只能以“从三岁时开始就是神枪手,汉诗倒背如流”、“金正恩会七国语言”等内容,向居民宣传金正恩的“伟大一面”。但没有一个朝鲜居民相信这些是真的。金正恩2010年在金日成诞辰前一天在平壤大同江畔花费60亿韩元举行了大型烟花晚会。朝鲜把这件事归为金正恩的业绩,但朝鲜居民纷纷反应“如果用那笔钱买玉米该多好啊”。

如今朝鲜居民对没有业绩的领导人既不尊敬也不爱戴。但在朝鲜所处的环境下,金正恩很难创造能获得国民支持的业绩。即使成功发起对南挑衅,也不能向居民宣传。因此,金正恩的生日只能在安静中度过。

了解朝鲜的人都知道,现在朝鲜到处流浪乞讨的叫花子越来越多了。而更严重的,是叫花子中相当多的人结伙进行偷盗等逐渐成为犯罪集团。消息人士说,“货币改革之后随着物价暴涨,事实上沦落为贫困层的家庭中父母去世后孩子离开家在街上流浪”;“这些叫花子遇到30或40多岁头目之后组成‘帮派’,成为小偷集团。”在平壤,连小叫花子们也结伙进行犯罪活动,居民感到极大的不安。可是,知道他们偷盗行为的安全部门,却不抓捕他们,反而将他们当做敲诈财物的工具。“保安部掌握犯罪动向后没有进行取缔反而偷偷找他们要钱。每次来就收取1万元左右,不断要金钱”;消息人士说,“2010年12月,平壤市寺洞区市场附近的街道里,商人们做完生意回家的路上,叫花子们拿着铁锤和木棍要钱,发生了打斗事件,多名商人受了伤。”南浦市也发生了类似事件。其他消息人士们也说,最近叫花子们不仅在市场乞讨,还有组织地进行扒窃和偷盗市场物品,所以商人们非常气愤。过去也有一些小偷,可是有头目、有组织地进行盗窃和抢钱物是最近的事情。带领他们的头目中规模最大的一天能赚1000美元,用这些钱收买保安官。

近来,朝鲜青少年吸食毒品“冰毒”的现象也大幅度增加。毒品甚至被称为朝鲜青少年最喜欢的生日礼物。消息人士称,对于朝鲜的青少年而言,毒品已经成为权力和财富的象征。 据消息人士介绍,党、法、教育部门联合组曾调查组,企图管制这一现象,但难以组织毒品的扩散。咸镜朝道消息人士称:“包括中学生在内的年轻人因着迷于‘冰毒’而导致人生破灭的现象开始出现,党、法、教育机关开始采取非常措施”;“近来由党务人员、法官等组成的调查组突袭位于咸镜朝道清津市浦项区的今日成铜像前的南江女子中学,对一个毕业班的女生书包进行了检查。结果有50%以上的女生书包中携带有可以吸食冰毒的用具。”

但由于被查出毒品用具的个多数为党政法机关干部们的子女或者有钱人家的孩子,调查当局也非常头痛。据悉,学生们通常利用便于携带的圆珠笔管儿或卷成管状的纸币(5000元面额,印有金日成的肖像)吸食毒品。消息人士称,“学生们是在仿效父母在家时的做法”;“监控没有多大效果”;以前学生们彼此将学用品或衣物等作为生日礼物,但是现在最受欢迎的生日礼物是毒品。学生们放学后没有人会想到学习,通常三五成群地熬夜吸食毒品。

据消息人士介绍,一些女生为了筹措毒资,甚至到站前等地方卖淫。近来审判所处理的案件中离婚等,与家庭不和、毒品有关的事件超过50%。而金正恩登场后,毒品调查组等各种管制虽然大为增加,但是人们不信任却反而在增加。因为管制毒品的人其实也是些瘾君子,干部们和上层人士们也有很多在吸毒,管制几乎没有什么效果。“就算多了几个最高领导人,也不可能管住毒品。“

据悉,在中国东北,只要500元人民币就能买一个十五、六岁美丽的朝鲜姑娘。那里的脱朝者只要不被送回去,有口饭吃就感恩戴德,一旦遗送回到朝鲜,重者枪毙,轻者劳改,生不如死。朝鲜现在处处体现“维稳”,金正恩推行“恐怖政治”。2010年被公开处决的居民达2009年的3倍以上,政府还下令“当场击毙脱朝者”,因此,在跨越鸭绿江、图们江的过程中送命的居民也大幅增加;比如2010年12月14日,经过冰冻的鸭绿江前往中国的7名朝鲜居民已进入中国境内,遭到朝鲜人民军的枪击,造成5人死亡、2人受伤,伤者最终还是被送回朝鲜。而朝鲜人民军也断了口粮,军队内部又掀起了“肃清风暴”。 金正恩已经拉开了红色恐怖的帷幕,以巩固权力基础。朝鲜张贴布告,指明有使用中国手机等行为会被“公开处决”。金正恩下令“要在全国听到枪声”。

朝鲜彻底镇压不安定因素,时隔30年首次修改的劳动党章程。劳动党章程第一章“党员义务”中加入如下条文:“党员应反对阶级敌人、各种异类思想和非社会主义现象等消极现象并与其作斗争。”看韩国电视剧或穿韩国风格服装的居民,也被扣上反党的帽子,成为公开处决的根据。2010年12月初,就有近200名高层干部被保卫部逮捕,有的被处决,有的被关进收容所。金正恩表示,“那些祖国有难时聚敛财富的人,无疑是在发生紧急状况时心中另有算盘的人”;他对保卫部下令,无条件逮捕并收押私藏5万美元以上资金的人。

人类历史已经证明,一切封建暴君都要给自己头上戴上最美丽的花环,各朝各代皇帝都说自己是真龙天子,赐死还要谢主龙恩。朝鲜王朝吗,金正日比金日成利害,看来金正恩比金正日更利害,又一个“红太阳”升起了,挂在地球东方。

上帝欲其灭亡,必先让其疯狂。

这就是当下疯狂的朝鲜,表面看似强悍,其内部虚弱不堪。

分类
标签
十二月
18
2011

京骚戏画

2
作者:AirForce

标签
十二月
2
2011

饭否再测试

2
作者:AirForce

再测试。。。

分类
标签
十二月
2
2011

饭否测试。。。

2
作者:AirForce

饭否测试。。。

分类
标签
十月
27
2011

OpenERP Magento Integration

3
作者:AirForce

几个python的模块:

http://bazaar.launchpad.net/~magentoerpconnect-core-editors/magentoerpconnect/trunk_version/files

https://code.launchpad.net/~magentoerpconnect-core-editors/magentoerpconnect/magentoerpconnect-v6

 

Magento is a feature-rich eCommerce platform built on open-source technology that provides online merchants with unprecedented flexibility and control over the look, content and functionality of their eCommerce store. Magento’s intuitive administration interface features powerful marketing, search engine optimization and catalog-management tools to give merchants the power to create sites that are tailored to their unique business needs. Designed to be completely scalable and backed by Varien’s support network, Magento offers companies the ultimate eCommerce solution.

 

A new bridge between OpenERP and Magento initiated by Openlabs team and under rapid improvement with the active support of community is now one of the most popular combinations for the ‘best of breed’ approach in ERP e-commerce deployment. This module allows synchronization of Magento with Open ERP. It supports Synchronization of Customer Groups, Product Categories, Product Attribute Sets, Attribute Groups, Product Attributes, Products, Order Statuses, Image Synchronization and many more

 

The bridge consists of two components: A Magento side extension and an Open ERP side module. The Magento extension is a PHP based web services improvement which is fully written and maintained by Openlabs. The Open ERP module has achieved further modularity and is today the preferred algorithm to quickly integrate Open ERP with other applications. Our latest in the list is a simple integration with Sales Force CRM.

 

Openlabs is actively involved in the Magento – Open ERP Projects and ever since the development of the new connector we have been helping customers across the globe to migrate and implement it.At Openlabs we are committed to provide an excellent user experience .The following detailed tutorial will guide you to the installation process of – :

    1. Magento (full version)
    1. Magento – Open ERP Connector module contributed by Openlabs
    1. Magento Extension

 

Step 1: Required bits & pieces

  1. Required Open ERP Addons (Available for download from this site):

2. Xampp 1.6.4

http://sourceforge.net/projects/xampp/files/XAMPP%20Linux/1.6.4/xampp-linux-1.6.4.tar.gz/download

After copying the addons to the Open ERP addons path,

  1. Go to a Linux shell and login as the system administrator root:
  2. For xampp-linux-1.6.4.tar.gz file follow these steps:-
  • a)Extract the downloaded archive file to /opt. using sudo tar xvfz xampp-linux-1.6.4.tar.gz -C /opt

3. Open the page http://localhost.It should show xampp on the page. You may get errors like shown below:

Warning: file_get_contents(lang.tmp) [function.file-get-contents]: failed to open stream: Permission denied in /opt/lampp/htdocs/xampp/index.php on line 2

Warning: Cannot modify header information – headers already sent by (output started at /opt/lampp/htdocs/xampp/index.php:2) in /opt/lampp/htdocs/xampp/index.php on line 4

Above Errors can be Removed by following step – :

Change the permissions by using chmod of the lampp folder in /opt to readable and writable. After this change the permissions of file config.inc.php in /opt/lampp/phpmyadmin to “read-only” and restart lampp.


 

Step2: Installation of Magento full version

1.Download magento-1.3.2.4.tar.gz and extract it to /opt/lampp/htdocs

sudo tar xvfz magento-1.3.2.4.tar.gz -C /opt/lampp/htdocs

3.To check your Magento installation type http://<yourIPaddress>/magento in the browser.

It should display the “Setup for Magento”/”Magento Installation” for the first time and “Magento Demo Store” from the next time.

Note: Use your IP instead of localhost to avoid issues of magento kicking you from the admin login.

If you get an error: innodb engine Open file xamppmysqlbinmy.cnf (Using sudo gedit or sudo nano)

Find code:

  • # Comment the following if you are using InnoDB tables
  • Skip-innodb
  • #innodb_data_home_dir = “/xampplite/mysql/”
  • #innodb_data_file_path = ibdata1:10M: autoextend
  • #innodb_log_group_home_dir = “/xampplite/mysql/”
  • #innodb_log_arch_dir = “/xampplite/mysql/”
  • ## You can set._buffer_pool_size up to 50 – 80 %
  • ## Of RAM but beware of setting memory usage too high
  • #innodb_buffer_pool_size = 16M
  • #innodb_additional_mem_pool_size = 2M
  • ## Set._log_file_size to 25 % of buffer pool size
  • #innodb_log_file_size = 5M
  • #innodb_log_buffer_size = 8M
  • #innodb_flush_log_at_trx_commit = 1
  • #innodb_lock_wait_timeout = 50

 

Modify to

  • #Comment the following if you are using InnoDB tables
  • #skip-innodb
  • innodb_data_home_dir = “/xampplite/mysql/”
  • innodb_data_file_path = ibdata1:10M: autoextend
  • innodb_log_group_home_dir = “/xampplite/mysql/”
  • innodb_log_arch_dir = “/xampplite/mysql/”
  • ## You can set._buffer_pool_size up to 50 – 80 %
  • ## Of RAM but beware of setting memory usage too high
  • innodb_buffer_pool_size = 16M
  • innodb_additional_mem_pool_size = 2M
  • ## Set…_log_file_size to 25 % of buffer pool size
  • innodb_log_file_size = 5M
  • innodb_log_buffer_size = 8M
  • innodb_flush_log_at_trx_commit = 1
  • innodb_lock_wait_timeout = 50

 

Reload the magento page, if some error of “mysql.sock not found” comes then change the rights of my.cnf file to read only, than reload the magento page and it should work now.


 

Step3:Configuring the Bridge

The Magento-Open ERP bridge comprises of two parts:

    1. Open ERP Module
    1. Magento ERP Extension

 

Installation of Magento extension

For this in magento admin panel go to:

System >> Magento Connect >> Magento Connect Manager

Key in your username and password again and click on the second tab for settings and change preferred state to ‘Beta’. (As of this date the plug-in is beta).Save your settings and select page 1 (Extensions) and paste the following extension key in the box

“Magento-community/Openlabs_OpenERPConnector” without quotes

Now your installation of magento is almost ready to talk to Open ERP

To enable open ERP to access the resources in magento and synchronize, it is necessary to have a web services user. To create a web services user go to : System >> Web Service >>Roles

Create a new role e.g. ‘admin’

Save the user and set resources access as: ALL

Save the role and now create a web services user at: System >> Web Services >> Users

Create a user, save the user and set the assigned role as the newly created role, in the above example ‘admin’

The same settings have to be entered as credentials in the connector frontend in Open ERP at Magento >> Magento Web

The user id will be the newly created user name and password the newly created password of the web services user.


 

STEP 4 :Configuration of Magento – Open ERP Connector module

1.Ensure that you have at least 1 Product category in your system or create one. (Required)

2.Install the magentoerpconnect module (For how to install a module refer to doc.openerp.com) (Module available at bzr branch lp: magentoerpconnect and/or bzr branch http://bazaar.launchpad.net/~openlabs-akretion-consortium/magentoerpconnect/magentoerpconnect_generic)

3.Go to Magento Connection > Core Settings > Magento Instances

4.Create a new instance by clicking new

5.Give the connection a name (e.g. My local magento)

6.Referential Type (Only 1.3.2.4 is available now)

7.Location: Your magento URL e.g. (http ://< yourIPaddress>/magento) & Default Product category (Magento allows products without category and those will be classified here)

8.Click on Reload Referential Mapping Templates

9.Enter API Username and password defined in step 4 of magento configuration

10.Click on Synchronize Referential Settings


 

Openlabs provides standard packages for implementation of Magento OpenERP connector with following services, provided by our expert technical team:

  • 1.Installation of Open ERP on production server.
  • 2.Implementation of Magento Open ERP Connector.
  • 3.Configuration & testing of the complete system as per the standard functionality.
  • 4.Detailed user guides & user trainings for using the system.
十月
27
2011

In this tutorial I’m presenting you a little weather widget, that could come handy for travel websites, or our personal page. If you make some changes on the code, you could show a different interface on your website, according to the weather on your city. Pretty cool stuff. We gonna connect weather.com XML data file with our flash widget. Weather.com offers us a free service to do that.

Adobe Flash CS3

Try / Buy

Source Files

Download

The first thing we gonna do is login in our weather.com account:

https://registration.weather.com/ursa/profile

Or if we already don’t have an account we could register for free in here:

https://registration.weather.com/ursa/profile/new?

Once we are in our account, we gonna have a page like this one, where on the left we gonna have all the services we are subscribed and on the right, all the available services, the XML data feed is not there, it’s a little bit tricky.

So, we gonna signup to the XML data feed on this page https://registration.weather.com/ursa/xmloap/Where we gonna tell to weather.com in which way we gonna use the data. If it’s for personal purpose, there’s no problem, but if it’s commercial purpose, you have to contact them, but everything is better explained on the mail you are going to receive.

So if our registration it’s ok, we gonna receive an email like this one (check on the spam inbox too, just in case).

The email explains us about the service, and there’s three important things to see in here:

  • Where to download the weather.com SDK (http://download.weather.com/web/xml/sdk.zip)
  • The weather.com XML Partner ID & License Key
  • How to make a properly formatted XML request for the service

On this package you gonna find a PDF, with the license agreement and some useful information about how to make a request for the services, and two folder, one with the weather.com logos, and the other with the weather icons we gonna use on this tutorial as reference. There’s 3 type of icon sizes, for this tutorial we gonna use the 93×93 icons. The icons are not too fancy, so we could download a free cool set of weather icons from here:

http://liquidweather.net/icons.php#iconsets

This iconsets and backgrounds are from the Liquid Weather++ appi.

We could put all our data inside flash, like the code of our city, our partner id, and so on, but it’s gonna be a pain in the ass every time we want to change it. So, it’s much better if we load this type of data externally form an XML file. The XML structure should looks like this:

	ARBA0009
XXXXXXXXX
	XXXXXXXXXX
	m

Now our hands on AS. We gonna do all the programming in just 2 frames, pretty easy. On the first frame, we load all the data to use it when we call the weather.com xml service. On the second frame, called “weather”, we load the weather.com xml and display the data. So, in our first frame:

  • We define the variables we gonna use
  • We call the xml that holds our data
  • Then we pass the data from the xml to our variables
  • And if everything it’s oki, we could go to the weather frame

stop();

//=================
//ini variables
//=================

//we define our variables to be loaded

var city:String;
var par_id:String;
var key:String;
var units:String;

//call the xml with our data

var data_xml_url:String = "data.xml";
var user_data:XML = new XML();
var data_url:URLRequest = new URLRequest(data_xml_url);
var dataLoader:URLLoader = new URLLoader(data_url);

dataLoader.addEventListener(Event.COMPLETE, dataLoaded);

function dataLoaded(e:Event):void
{
	user_data = XML(dataLoader.data);

	//pass the data from the XML to our variables

	city = user_data.city.toString();
	par_id  = user_data.parid.toString();
	key  = user_data.key.toString();
	units  = user_data.units.toString();

	//if everything it's oki, we could go to the weather frame

	gotoAndStop("weather");
}

To display the different type of weather, we need a movieClip that holds all the possible weathers, there’s not too much, just 47. So, we create a new movieclip and call it “icons_mc”, and inside the icons_mc we import all the icons from the Weather.com SDK icons folder, or from the cool icons from liquidweather. There’s a lil bit differences between the Weather.com SDK icons and the liquidweather icons. On the weather.com SDK, the icons 25 and 44 are N/A, if you gonna use the liquidweather icons, just replace the 25 and 44 icons with the N/A png. Don’t forget to put a stop(); in the first frame of the icons_mc, so the MC start stopped.

Now we could load the data from weather.com. We allow our swf to make calls from the domain “xoap.weather.com”, we make the icons_mc MovieClip invisible, because we don’t know which icon use yet, and we compose the weather xml url with the variables from the frame 1.

stop();
//=================
//allow domains
//=================
Security.allowDomain("*", "xoap.weather.com");
//=================
//ini settings
//=================
icons_mc.visible = false;
//=================
//XML
//=================
var weather_xml_url:String = "http://xoap.weather.com/weather/local/"+city+"?cc=*&link=xoap&par="+par_id+"&key="+key+"&unit="+units;

The xml from weather.com have this structure with some interesting data. As you can see there’s a lot of useful data to load, but we gonna load just the necessary by now. Later you could play with it and make an advanced one.

en_US
MEDIUM
    C
    km
    km/h
    mb
    mm

    Buenos Aires, Argentina
    1:39 PM
    -34.61
    -58.37
    8:01 AM
    5:52 PM
    -3

    6/27/08 1:00 PM Local Time
    Buenos Aires Air Park, Argentina
    13
    13
    Fog
    20

      1021.0
      falling

      11
      N/A
      360
      N

    82
    2.5

      2
      Low

    10

      23
      Waning Crescent

To display the data, we have three different sprites:

  • The icons MovieClip
  • A dynamic text field to load the outside temp called “temp_txt”
  • A dynamic text filed in the bottom to make a lil speech about how feels to be outside called “info_txt”

Our main interest is on the XML file on the icon number, so we could show the right icon. We load the xml using our well formatted “weather_xml_url”, and traverse the xml three to find what we are looking for.

In this step we:

  • we load the temp on the temp_txt text field
  • make the icons visible, load the icon number and tell the icons:mc to stopn on thet icon frame
  • and finally we set the complementary text
var weather:XML = new XML();
var weather_url:URLRequest = new URLRequest(weather_xml_url);
var weatherLoader:URLLoader = new URLLoader(weather_url);

weatherLoader.addEventListener(Event.COMPLETE, weatherLoaded);

function weatherLoaded(e:Event):void
{
	weather = XML(weatherLoader.data);

	//we load the temp on the temp_txt text field
	temp_txt.text = weather.cc.tmp;

	//we make the icons visible, load the icon number and tell the icons:mc to stopn on thet icon frame
	icons_mc.visible = true;

	var weather_icon:int = Number(weather.cc.icon.toString())+1;
	icons_mc.gotoAndStop(weather_icon);

	//and finally we set the complementary text
	var ud:String = weather.head.ud;
	var us:String = weather.head.us;

	var city:String = weather.loc.dnam;
	var time:String = weather.loc.tm;
	var temp:String = weather.cc.tmp;
	var flik:String = weather.cc.flik;
	var term:String;

	if (temp == flik)
	{
		term = "and it feels like "+flik+" degrees,";
	} else {
		term = "but it feels like "+flik+" degrees,";
	}

	var wind_v:String = weather.cc.wind.s;
	var wind_gust:String = weather.cc.wind.gust;
	var wind_d:String = weather.cc.wind.d;
	var wind_t:String = weather.cc.wind.t;

	var hmid:String = weather.cc.hmid;
	var vis:String = weather.cc.vis;

	info_txt.text = "It's "+time+" here in "+city+". It's "+temp+" degrees out there "+term+" the wind blows from the "+wind_t+" at "+wind_v+" "+us+", the humidity is "+hmid+"% and the visibility is "+vis+" "+ud+".";
}

Now we could export our movie and if it’s cold outside, it’s good to be inside.

We gonna need to know the international code of our city, so we could go tohttp://www.weather.com/common/welcomepage/world.html Search for our city, and find the code on our address bar. I know it’s a lil bit tricky but it works fine.

Hope you enjoyed this lil tutorial as much as I enjoyed writing for you.

十月
27
2011

If you have ever wanted to manipulate images under linux you probably have used Gimp. This isn’t your only option and if you want to do things from the command line a better option is to use ImageMagick‘s convert utility.

I’ve put together 5 simple command line examples that I have found useful. This is just a sample of what you can do with convert. To see more examples and get more explanation of options see: ImageMagick v6 Examples.

 

I started with the following image as a base for all the examples that follow:

1. Text annotations

Example (simple text in static location):

convert flower.jpg -font courier -fill white -pointsize 20 -annotate +50+50 ‘Flower’ flower_annotate1.jpg

Produces:

Example (text with background at bottom):

convert flower.jpg -fill white -box ‘#00770080′ -gravity South -pointsize 20 -annotate +0+5 ‘   Flower  ’ flower_annotate2.jpg

Produces:

Look at these examples to see more.

2. Cropping an image

Example:

convert flower.jpg -crop 128×128+50+50 flower_crop.jpg

Produces:

Look at these examples or -crop for more information.

3. Rotate an image

Example:

convert flower.jpg -rotate 45 flower_rotate45.jpg

Produces:

Look at these examples or -rotate for more information.

4. Image montage

Example:

montage flower.jpg flower_unsharp.jpg -geometry ’300×200+20+20′ flower_montage.jpg

Produces:

Look at these examples to see more.

5. Animation

Example:

convert flower.jpg -resize 100×100 -font courier -fill white -pointsize 20 -annotate +50+50 ‘Frame 1′ flower_frame1.gif
convert flower.jpg -resize 100×100 -font courier -fill white -pointsize 20 -annotate +50+50 ‘Frame 2′ flower_frame2.gif
convert flower.jpg -resize 100×100 -font courier -fill white -pointsize 20 -annotate +50+50 ‘Frame 3′ flower_frame3.gif
convert flower.jpg -resize 100×100 -font courier -fill white -pointsize 20 -annotate +50+50 ‘Frame 4′ flower_frame4.gif
convert -delay 100 -size 100×100 \
-page +0+0 flower_frame1.gif \
-page +0+0 flower_frame2.gif \
-page +0+0 flower_frame3.gif \
-page +0+0 flower_frame4.gif \
-loop 0 flower_animation.gif

Produces:

十月
27
2011

A while back I explained how to compile the ImageMagick extension for PHPand this past week I got around to creating some example code to make some of the command line examples I have in ImageMagick command line examples part 1 and ImageMagick command line examples part 2.

The first step of course is to make sure the MagickWand extension is installed. You will want to verify that it is listed in a phpinfo() call before trying any of these examples. After verifying that you have the extension installed you might want to read an introduction to using MagickWand before looking at these examples. And of course you will want to know where the MagickWand reference documentation is once you are ready to try more.

For more information on the options used in these examples it is best to look at their corresponding command line example.

Example 1: Simple Annotate

<?php 

// convert flower.jpg -font courier -fill white -pointsize 20 -annotate +50+50 Flower flower_annotate1.jpg

$resource = NewMagickWand();
$dwand = NewDrawingWand();
$pwand = NewPixelWand();

PixelSetColor($pwand, ”white”);
DrawSetFont($dwand,”/usr/share/fonts/default/TrueType/cour.ttf”);
DrawSetFontSize($dwand, 20);
DrawSetFillColor($dwand, $pwand);

MagickReadImage( $resource, ’small_flower.jpg’ );

if( MagickAnnotateImage( $resource, $dwand, 0, 0, 0, ”Flower” ))
{
header( ’Content-Type: image/gif’ );
MagickEchoImageBlob( $resource );
}
else
{
echo MagickGetExceptionString($resource);
}

?>

One note on the above is that I needed to specify the exact location of the font to get it to show up. I believe this isn’t always needed but if you try to leave it out and nothing shows up you should try specifying the full path to the font.

Example 2: Complex Annotate

<?php 

// convert flower.jpg -fill white -box “#00770080″ -gravity South -pointsize 20 -annotate +0+5 ”   Flower  ” flower_annotate2.jpg

$resource = NewMagickWand();
$dwand = NewDrawingWand();
$pwand = NewPixelWand();

PixelSetColor($pwand, ”white”);
DrawSetFont($dwand,”/usr/share/fonts/default/TrueType/cour.ttf”);
DrawSetFontSize($dwand, 20);
DrawSetFillColor($dwand, $pwand);

DrawSetGravity($dwand, MW_SouthGravity);

MagickReadImage( $resource, ’small_flower.jpg’ );

if( MagickAnnotateImage( $resource, $dwand, 0, 0, 0, ”Flower” ))
{
header( ’Content-Type: image/gif’ );
MagickEchoImageBlob( $resource );
}
else
{
echo MagickGetExceptionString($resource);
}

?>

Example 3: Crop an Area

<?php 

// convert flower.jpg -crop 128×128+50+50 flower_crop.jpg

$resource = NewMagickWand();

MagickReadImage( $resource, ’small_flower.jpg’ );

if( MagickCropImage( $resource, 128, 128, 50, 50 ) )
{
header( ’Content-Type: image/gif’ );
MagickEchoImageBlob( $resource );
}
else
{
echo MagickGetExceptionString($resource);
}

?>

Example 4: Rotate

<?php 

// convert flower.jpg -rotate 45 flower_rotate45.jpg

$resource = NewMagickWand();
MagickReadImage( $resource, ’small_flower.jpg’ );

MagickRotateImage( $resource, null, 45 );

header( ’Content-Type: image/gif’ );
MagickEchoImageBlob( $resource );

?>

Example 5: Resize

<?php 

// convert flower_original.jpg -resize 640×480 flower.jpg

$resource = NewMagickWand();
MagickReadImage( $resource, ’small_flower.jpg’ );

MagickResizeImage( $resource, 100, 100, MW_QuadraticFilter, 1.0);

header( ’Content-Type: image/gif’ );
MagickEchoImageBlob( $resource );

?>

Example 6: Apply Resharp Filter

<?php 

// convert flower.jpg -unsharp 1.5×1.0+1.5+0.02 flower_unsharp.jpg

$resource = NewMagickWand();
MagickReadImage( $resource, ’small_flower.jpg’ );

MagickUnsharpMaskImage( $resource, 1.5, 1.0, 1.5, 0.02 );

header( ’Content-Type: image/gif’ );
MagickEchoImageBlob( $resource );

?>

Example 7: Compress JPG

<?php 

// convert flower.jpg -quality 80% flower_quality.jpg

$resource = NewMagickWand();
MagickReadImage( $resource, ’small_flower.jpg’ );

MagickSetFormat($resource, ’JPG’);
MagickSetImageCompression($resource, MW_JPEGCompression);
MagickSetImageCompressionQuality($resource, 80.0);

header( ’Content-Type: image/gif’ );
MagickEchoImageBlob( $resource );

?>

页面: 1 2 3 4 5 6 7 8 9 10 ...84 85 86 下一页