Heim >Backend-Entwicklung >PHP-Tutorial >Vertiefte Kenntnisse der PHP-Erweiterungsentwicklung
PHP ist derzeit eine sehr weit verbreitete Sprache. Sie ist auf verschiedenen großen, mittleren und kleinen Websites zu sehen, von ausländischem Facebook und Twitter bis hin zu inländischen Taobao, Tencent, Baidu und allen Arten großer, mittlerer und kleiner Websites das Internet. Es sollte gesagt werden, dass der Erfolg von PHP weitgehend von seinem offenen Erweiterungs-API-Mechanismus und seinen umfangreichen Erweiterungskomponenten (PHP-Erweiterung) abhängt. Es sind diese Erweiterungskomponenten, die es PHP ermöglichen, von verschiedenen Datenbankoperationen bis hin zu XML, JSON, Verschlüsselung, Dateiverarbeitung usw. zu arbeiten. Es ist in Bereichen wie Grafikverarbeitung und Socket allmächtig. Manchmal müssen Entwickler ihre eigenen PHP-Erweiterungen entwickeln. Der aktuelle Erweiterungsmechanismus von PHP5 basiert auf der Zend-API und bietet umfangreiche Schnittstellen und Makrodefinitionen sowie einige praktische Tools, sodass die Entwicklung von PHP-Erweiterungen nicht besonders schwierig ist. In diesem Artikel werden grundlegende Kenntnisse über die Entwicklung von PHP-Erweiterungskomponenten vermittelt und der grundlegende Prozess der Entwicklung von PHP-Erweiterungen anhand eines Beispiels demonstriert.
Der Entwicklungsprozess von PHP-Erweiterungskomponenten unterscheidet sich in Unix- und Windows-Umgebungen, sie sind jedoch grundsätzlich interoperabel. Dieser Artikel basiert auf der Unix-Umgebung (insbesondere unter Verwendung von Linux). Das Lesen dieses Artikels erfordert ein einfaches Verständnis einiger Grundkenntnisse der Unix-Umgebung, PHP und der C-Sprache. Solange Sie über ein einfaches Verständnis verfügen, werde ich versuchen, nicht zu sehr auf bestimmte Betriebssystem- und Sprachfunktionen einzugehen und diese bei Bedarf zu erläutern erleichtern den Lesern das Lesen.
Die spezifische Entwicklungsumgebung dieses Artikels ist Ubuntu 10.04 + PHP 5.3.3.
Um PHP-Erweiterungen zu entwickeln, besteht der erste Schritt darin, den PHP-Quellcode herunterzuladen, da dieser die für die Entwicklung von Erweiterungen erforderlichen Tools enthält. Was ich heruntergeladen habe, ist die neueste Version von PHP 5.3.3 im Format des komprimierten Pakets tar.bz2.
Die Download-Adresse lautet: http://cn.php.net/get/php-5.3.3.tar.bz2/from/a/mirror
Zum Schluss den Quellcode in das entsprechende Verzeichnis verschieben und entpacken. Der Dekomprimierungsbefehl lautet:
tar -jxvf Quellpaketname
Wenn Sie ein komprimiertes tar.gz-Paket heruntergeladen haben, lautet der Dekomprimierungsbefehl
tar - zxvf Quellpaketname
Nach der Dekomprimierung gibt es im Quellcodeverzeichnis ein ext-Verzeichnis, das sich auf die PHP-Erweiterung bezieht. Nachdem Sie das Verzeichnis eingegeben und mit l angezeigt haben, können Sie viele vorhandene Erweiterungen sehen. Das Bild unten ist das Ergebnis der Betrachtung in meiner Umgebung:
Die blauen sind die Erweiterungspaketverzeichnisse, in denen Sie die bekannten Dateien mysql, iconv und gd usw. sehen können . ext_skel ist ein Skripttool zum automatischen Generieren eines PHP-Erweiterungsframeworks in einer Unix-Umgebung. Wir werden es bald verwenden. ext_skel_win32.php ist das entsprechende Skript unter Windows.
Jetzt entwickeln wir eine PHP-Erweiterung: say_hello. Diese Erweiterung ist sehr einfach, sie akzeptiert lediglich einen String-Parameter und gibt „Hallo xxx!“ aus. Dieses Beispiel dient nur zur Einführung in den Entwicklungsprozess von PHP-Erweiterungskomponenten und übernimmt keine tatsächlichen Funktionen.
Das Erweiterungskomponenten-Entwicklungsverzeichnis und die Dateien haben eine feste Organisationsstruktur. Sie können alle darin enthaltenen Dateien anzeigen war blendend. Natürlich können Sie das Framework auch manuell erstellen, aber ich glaube, Sie möchten es lieber von jemandem erledigen lassen. Das oben erwähnte ext_skel-Skript ist ein Tool zum automatischen Erstellen des Erweiterungspaket-Frameworks. Der vollständige Befehl von ext_skel lautet:
ext_skel --extname=module [--Proto=Datei] [-- Stubs=Datei] [--xml [=Datei]] [--skel=dir] [--full-xml] [--nein-Hilfe]
Als Anfänger müssen wir nicht alle Befehlsparameter kennen. Tatsächlich müssen wir in den meisten Fällen nur den ersten Parameter angeben der Name des Erweiterungsmoduls. Dazu geben wir im ext-Verzeichnis folgenden Befehl ein:
./ext_skel -- extname =say_hello
(Wenn Sie mehr über die verschiedenen Befehlsparameter von ext_skel erfahren möchten, lesen Sie bitte hier)
Verwenden Sie zu diesem Zeitpunkt ls, um es anzuzeigen. Sie werden feststellen, dass es ein zusätzliches Verzeichnis „say_hello“ gibt. Wenn Sie dieses Verzeichnis betreten, werden Sie feststellen, dass ext_skel das Grundgerüst von say_hello für uns erstellt hat unten gezeigt:
Wenn Sie zu faul sind, den gesamten Inhalt der Verzeichnisstruktur des PHP-Erweiterungspakets herauszufinden, dann gibt es darin drei Dateien, auf die Sie achten müssen :
config.m4: Dies ist die Build-System-Konfigurationsdatei in der Unix-Umgebung, gefolgt von Konfiguration und Installation wird über sie generiert.
php_say_hello.h: Diese Datei ist die Header-Datei des Erweiterungsmoduls. Dem konsistenten Stil der C-Sprache folgend, können darin einige benutzerdefinierte Strukturen, globale Variablen usw. platziert werden.
say_hello.c: Dies ist die Hauptprogrammdatei des Erweiterungsmoduls. Die letzten Funktionseinträge des Erweiterungsmoduls sind hier. Natürlich können Sie den gesamten Programmcode hineinstecken oder dem modularen Gedanken folgen und jedes Funktionsmodul in verschiedene Dateien legen.
Der folgende Inhalt dreht sich hauptsächlich um diese drei Dateien.
Der erste Schritt bei der Entwicklung von PHP-Erweiterungskomponenten besteht nicht darin, den Implementierungscode zu schreiben, sondern zunächst die Build-System-Optionen zu konfigurieren. Da wir unter Linux entwickeln, bezieht sich die Konfiguration hier hauptsächlich auf config.m4.
Was die Build-System-Konfiguration betrifft: Wenn ich viel schreiben kann und sie mit vielen Dingen im Unix-System zusammenhängt, selbst wenn ich daran interessiert bin, sie zu schreiben, wird vermutlich nicht jeder daran interessiert sein, sie zu lesen Daher lassen wir es hier weg und wählen nur die wichtigsten Punkte aus. Weitere Einzelheiten zu config.m4 finden Sie hier.
Öffnen Sie die generierte Datei config.m4. Der Inhalt lautet ungefähr wie folgt:
dnl $Id$ dnl config.m4 for extension say_hello dnl Comments in this file start with the string 'dnl'. dnl Remove where necessary. This file will not work dnl without editing. dnl If your extension references something external, use with: dnl PHP_ARG_WITH(say_hello, for say_hello support, dnl Make sure that the comment is aligned: dnl [ --with-say_hello Include say_hello support]) dnl Otherwise use enable: dnl PHP_ARG_ENABLE(say_hello, whether to enable say_hello support, dnl Make sure that the comment is aligned: dnl [ --enable-say_hello Enable say_hello support]) if test "$PHP_SAY_HELLO" != "no"; then dnl Write more examples of tests here... dnl # --with-say_hello -> check with-path dnl SEARCH_PATH="/usr/local /usr" # you might want to change this dnl SEARCH_FOR="/include/say_hello.h" # you most likely want to change this dnl if test -r $PHP_SAY_HELLO/$SEARCH_FOR; then # path given as parameter dnl SAY_HELLO_DIR=$PHP_SAY_HELLO dnl else # search default path list dnl AC_MSG_CHECKING([for say_hello files in default path]) dnl for i in $SEARCH_PATH ; do dnl if test -r $i/$SEARCH_FOR; then dnl SAY_HELLO_DIR=$i dnl AC_MSG_RESULT(found in $i) dnl fi dnl done dnl fi dnl dnl if test -z "$SAY_HELLO_DIR"; then dnl AC_MSG_RESULT([not found]) dnl AC_MSG_ERROR([Please reinstall the say_hello distribution]) dnl fi dnl # --with-say_hello -> add include path dnl PHP_ADD_INCLUDE($SAY_HELLO_DIR/include) dnl # --with-say_hello -> check for lib and symbol presence dnl LIBNAME=say_hello # you may want to change this dnl LIBSYMBOL=say_hello # you most likely want to change this dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, dnl [ dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SAY_HELLO_DIR/lib, SAY_HELLO_SHARED_LIBADD) dnl AC_DEFINE(HAVE_SAY_HELLOLIB,1,[ ]) dnl ],[ dnl AC_MSG_ERROR([wrong say_hello lib version or lib not found]) dnl ],[ dnl -L$SAY_HELLO_DIR/lib -lm dnl ]) dnl dnl PHP_SUBST(SAY_HELLO_SHARED_LIBADD) PHP_NEW_EXTENSION(say_hello, say_hello.c, $ext_shared) fi
Lesen Sie nicht so viel, weil Alles beginnt mit „dnl“. Der Anfang besteht ausschließlich aus Kommentaren, daher funktionieren nur wenige Zeilen tatsächlich. Hier müssen lediglich die folgenden Zeilen konfiguriert werden:
dnl If your extension references something external, use with: dnl PHP_ARG_WITH(say_hello, for say_hello support, dnl Make sure that the comment is aligned: dnl [ --with-say_hello Include say_hello support]) dnl Otherwise use enable: dnl PHP_ARG_ENABLE(say_hello, whether to enable say_hello support, dnl Make sure that the comment is aligned: dnl [ --enable-say_hello Enable say_hello support])
Ich denke, jeder kann es verstehen, was bedeutet „Wenn Ihre Erweiterung auf externe Komponenten verweist, verwenden..., andernfalls verwenden...". Unsere say_hello-Erweiterung verweist nicht auf externe Komponenten. Entfernen Sie daher das „dnl“ in den drei Zeilen unter „Otherwise use enable“ und ändern Sie es in:
dnl Otherwise use enable: PHP_ARG_ENABLE(say_hello, whether to enable say_hello support, Make sure that the comment is aligned: [ --enable-say_hello Enable say_hello support])
Speichern, und die Build-System-Konfiguration ist abgeschlossen.
Das Obige kann als Vorbereitung für die Entwicklung von PHP-Erweiterungen angesehen werden. Als nächstes werden wir den Kerncode schreiben. Wie oben erwähnt, basiert das Schreiben von PHP-Erweiterungen auf der Zend-API und einigen Makros. Wenn wir also Kerncode schreiben möchten, müssen wir zunächst die Struktur der PHP-Erweiterung herausfinden. Denn eine PHP-Erweiterung ist eigentlich eine zend_module_entry-Struktur auf der C-Sprachebene, was anhand von „php_say_hello.h“ bestätigt werden kann. Öffnen Sie „php_say_hello.h“ und Sie sehen darin eine Zeile:
extern zend_module_entry say_hello_module_entry;
say_hello_module_entry ist das C-Sprache entsprechende Element der say_hello-Erweiterung, und die Definition seines Typs zend_module_entry kann in der Datei „Zend/zend_modules.h“ des PHP-Quellcodes gefunden werden Code ist zend_module_entry Definition:
typedef struct _zend_module_entry zend_module_entry; struct _zend_module_entry { unsigned short size; unsigned int zend_api; unsigned char zend_debug; unsigned char zts; const struct _zend_ini_entry *ini_entry; const struct _zend_module_dep *deps; const char *name; const struct _zend_function_entry *functions; int (*module_startup_func)(INIT_FUNC_ARGS); int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); int (*request_startup_func)(INIT_FUNC_ARGS); int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS); void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS); const char *version; size_t globals_size; #ifdef ZTS ts_rsrc_id* globals_id_ptr; #else void* globals_ptr; #endif void (*globals_ctor)(void *global TSRMLS_DC); void (*globals_dtor)(void *global TSRMLS_DC); int (*post_deactivate_func)(void); int module_started; unsigned char type; void *handle; int module_number; char *build_id; };
Diese Struktur mag etwas verwirrend erscheinen, aber ich möchte trotzdem erklären, was drin ist. Da dies der Prototyp der PHP-Erweiterung ist, können Sie keine PHP-Erweiterung entwickeln, wenn Sie ihn nicht verstehen. Natürlich werde ich nicht jedes Feld einzeln erläutern, sondern nur die Schlüsselfelder auswählen, die in diesem Artikel verwendet werden, da viele Felder nicht manuell ausgefüllt werden müssen, sondern bestimmte vordefinierte Makros verwenden können.
Das siebte Feld „Name“, dieses Feld ist der Name dieser PHP-Erweiterung, in diesem Fall ist es „say_hello“.
Im 8. Feld „Funktionen“ werden Verweise auf die Funktionen gespeichert, die wir in dieser Erweiterung definieren. Interessierte Freunde können den Quellcode von _zend_function_entry lesen. Beim Schreiben von spezifischem Code gibt es hier entsprechende Makros.
Das 9. bis 12. Feld sind jeweils vier Funktionszeiger. Diese vier Funktionen werden zum entsprechenden Zeitpunkt aufgerufen, nämlich „wenn das Erweiterungsmodul geladen wird“, „wenn das Erweiterungsmodul entladen wird“ und „jeder Anfrage „Am Anfang“ und „Am Ende jeder Anfrage“. Diese vier Funktionen können als Abfangmechanismus betrachtet werden, der hauptsächlich für die Ressourcenzuweisung, -freigabe und andere damit verbundene Vorgänge zu entsprechenden Zeiten verwendet wird.
Das 13. Feld „info_func“ ist auch ein Funktionszeiger. Die Funktion, auf die dieser Zeiger zeigt, wird aufgerufen, wenn phpinfo() ausgeführt wird, um benutzerdefinierte Modulinformationen anzuzeigen.
Das 14. Feld „Version“ ist die Version des Moduls.
(Eine detailliertere Einführung in zend_module_entry finden Sie hier)
Nach der Einführung der obigen Felder können wir einen Blick auf den Framework-Code „say_hello_module_entry“ werfen, der automatisch in „say_hello.c“ generiert wird ".
/* {{{ say_hello_module_entry */ zend_module_entry say_hello_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "say_hello", say_hello_functions, PHP_MINIT(say_hello), PHP_MSHUTDOWN(say_hello), PHP_RINIT(say_hello), /* Replace with NULL if there's nothing to do at request start */ PHP_RSHUTDOWN(say_hello), /* Replace with NULL if there's nothing to do at request end */ PHP_MINFO(say_hello), #if ZEND_MODULE_API_NO >= 20010901 "0.1", /* Replace with version number for your extension */ #endif STANDARD_MODULE_PROPERTIES }; /* }}} */
首先,宏“STANDARD_MODULE_HEADER”会生成前6个字段,“STANDARD_MODULE_PROPERTIES ”会生成“version”后的字段,所以现在我们还不用操心。而我们关心的几个字段,也都填写好或由宏生成好了,并且在“say_hello.c”的相应位置也生成了几个函数的框架。这里要注意,几个宏的参数均为“say_hello”,但这并不表示几个函数的名字全为“say_hello”,C语言中也不可能存在函数名重载机制。实际上,在开发PHP Extension的过程中,几乎处处都要用到Zend里预定义的各种宏,从全局变量到函数的定义甚至返回值,都不能按照“裸写”的方式来编写C语言,这是因为PHP的运行机制可能会导致命名冲突等问题,而这些宏会将函数等元素变换成一个内部名称,但这些对程序员都是透明的(除非你去阅读那些宏的代码),我们通过各种宏进行编程,而宏则为我们处理很多内部的东西。
写到这里,我们的任务就明了了:第一,如果需要在相应时机处理一些东西,那么需要填充各个拦截函数内容;第二,编写say_hello的功能函数,并将引用添加到say_hello_functions中。
因为say_hello扩展在各个生命周期阶段并不需要做操作,所以我们只编写info_func的内容,上文说过,这个函数将在phpinfo()执行时被自动调用,用于显示扩展的信息。编写这个函数会用到四个函数:
php_info_print_table_start()——开始phpinfo表格。无参数。
php_info_print_table_header()——输出表格头。第一个参数是整形,指明头的列数,然后后面的参数是与列数等量的(char*)类型参数用于指定显示的文字。
php_info_print_table_row()——输出表格内容。第一个参数是整形,指明这一行的列数,然后后面的参数是与列数等量的(char*)类型参数用于指定显示的文字。
php_info_print_table_end()——结束phpinfo表格。无参数。
下面是“say_hello.c”中需要编写的info_func的具体代码:
/* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(say_hello) { php_info_print_table_start(); php_info_print_table_header(2, "say_hello support", "enabled"); php_info_print_table_row(2, "author", "Zhang Yang"); /* Replace with your name */ php_info_print_table_end(); /* Remove comments if you have entries in php.ini DISPLAY_INI_ENTRIES(); */ } /* }}} */
可以看到我们编写了两行内容、组件是否可用以及作者信息。
编写核心函数,总共分为三步:1、使用宏PHP_FUNCTION定义函数体;2、使用宏ZEND_BEGIN_ARG_INFO和ZEND_END_ARG_INFO定义参数信息;3、使用宏PHP_FE将函数加入到say_hello_functions中。下面分步说明。
PHP_FUNCTION(say_hello_func) { char *name; int name_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { return; } php_printf("Hello %s!", name); RETURN_TRUE; }
上文说过,编写PHP扩展时几乎所有东西都不能裸写,而是必须使用相应的宏。从上面代码可以清楚看到这一点。总体来说,核心函数代码一般由如下几部分构成:
定义函数,这一步通过宏PHP_FUNCTION实现,函数的外部名称就是宏后面括号里面的名称。
声明并定义局部变量。
解析参数,这一步通过zend_parse_parameters函数实现,这个函数的作用是从函数用户的输入栈中读取数据,然后转换成相应的函数参数填入变量以供后面核心功能代码使用。zend_parse_parameters的第一个参数是用户传入参数的个数,可以由宏“ZEND_NUM_ARGS() TSRMLS_CC”生成;第二个参数是一个字符串,其中每个字母代表一个变量类型,我们只有一个字符串型变量,所以第二个参数是“s”;最后各个参数需要一些必要的局部变量指针用于存储数据,下表给出了不同变量类型的字母代表及其所需要的局部变量指针。
参数解析完成后就是核心功能代码,我们这里只是输出一行字符,php_printf是Zend版本的printf。
最后的返回值也是通过宏实现的。RETURN_TRUE宏是返回布尔值“true”。
参数信息是函数所必要部分,这里不做深究,直接给出相应代码:
ZEND_BEGIN_ARG_INFO(arginfo_say_hello_func, 0) ZEND_END_ARG_INFO()
如需了解具体信息请阅读相关宏定义。
最后,我们需要将刚才定义的函数和参数信息加入到say_hello_functions数组里,代码如下:
const zend_function_entry say_hello_functions[] = { PHP_FE(say_hello_func, arginfo_say_hello_func) {NULL, NULL, NULL} };
这一步就是通过PHP_EF宏实现,注意这个数组最后一行必须是{NULL, NULL, NULL} ,请不要删除。
下面是编写完成后的say_hello.c全部代码:
/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: ZhangYang | +----------------------------------------------------------------------+ */ /* $Id: header 297205 2010-03-30 21:09:07Z johannes $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "php_say_hello.h" /* If you declare any globals in php_say_hello.h uncomment this: ZEND_DECLARE_MODULE_GLOBALS(say_hello) */ /* True global resources - no need for thread safety here */ static int le_say_hello; /* {{{ PHP_FUNCTION */ PHP_FUNCTION(say_hello_func) { char *name; int name_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { return; } php_printf("Hello %s!", name); RETURN_TRUE; } ZEND_BEGIN_ARG_INFO(arginfo_say_hello_func, 0) ZEND_END_ARG_INFO() /* }}} */ /* {{{ say_hello_functions[] * * Every user visible function must have an entry in say_hello_functions[]. */ const zend_function_entry say_hello_functions[] = { PHP_FE(say_hello_func, arginfo_say_hello_func) {NULL, NULL, NULL} /* Must be the last line in say_hello_functions[] */ }; /* }}} */ /* {{{ say_hello_module_entry */ zend_module_entry say_hello_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "say_hello", say_hello_functions, NULL, NULL, NULL, NULL, PHP_MINFO(say_hello), #if ZEND_MODULE_API_NO >= 20010901 "0.1", /* Replace with version number for your extension */ #endif STANDARD_MODULE_PROPERTIES }; /* }}} */ #ifdef COMPILE_DL_SAY_HELLO ZEND_GET_MODULE(say_hello) #endif /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(say_hello) { php_info_print_table_start(); php_info_print_table_header(2, "say_hello support", "enabled"); php_info_print_table_row(2, "author", "Zhang Yang"); /* Replace with your name */ php_info_print_table_end(); /* Remove comments if you have entries in php.ini DISPLAY_INI_ENTRIES(); */ } /* }}} */
在say_hello目录下输入下面命令:
/usr/bin/phpize
./configure
make
make install
这样就完成了say_hello扩展的安装(如果没有报错的话)。
这时如果你去放置php扩展的目录下,会发现多了一个say_hello.so的文件。如下图所示:
下面就是将其加入到php.ini配置中,然后重启Apache(如果需要的话)。这些都是PHP基本配置的内容,我就不详述了。
如果上面顺利完成,这时运行phpinfo(),应该能看到如下信息:
这说明扩展已经安装成功了。然后我们编写一个测试用PHP脚本:
<?php say_hello_func('Zhang Yang'); ?>;
执行这个脚本,结果如下:
说明扩展已经正常工作了。
这篇文章主要用示例方法介绍PHP Extension的开发基础。在PHP的使用中,也许是因为需要支持新的组件(如新的数据库),又或是业务需要或性能需要,几乎都会遇到需要开发PHP扩展的地方。后续如果有机会,我会写文章介绍一些关于扩展开发较为深入的东西,如扩展模块生命周期、INI使用以及编写面向对象的扩展模块等等。
Das obige ist der detaillierte Inhalt vonVertiefte Kenntnisse der PHP-Erweiterungsentwicklung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!