1. Write it first
With the rapid development of the Internet and the popularity of lamp architecture, PHP supports more and more extensions, which directly promotes the development of PHP.
However, PHP also has inevitable problems with scripting languages. Its performance is much different than compiled languages such as C, so when considering performance issues, it is best to solve them through PHP extensions.
So, how to make a php extension. Let's start with an example (this article requires C basics).
2. Solve a problem
In a system, if the sum of squares of an array is often required, we can write it like this.
- function array_square_sum($data){
- $sum = 0;
- foreach($data as $value){
- $sum += $value * $value;
- }
- return $sum ;
- }
Copy the code During actual execution, the php zend engine will translate this paragraph into C language, and memory allocation is required every time. So the performance is relatively poor. Furthermore, based on performance considerations, we can write an extension to do this.
3. Write extensions
To build an extension, at least 2 files are required. One is the Configulator file, which tells the compiler which at least dependent libraries are required to compile this extension; the second is the actual execution file.
3.1 Generate framework
It sounds complicated, but there is actually a tool that can help us figure out an extended framework. There is a tool ext_skel in the PHP source code, which can help us generate an extension framework. - liujun@ubuntu:~/test/php-5.5.8/ext$ ls ext_skel
- ext_skel
Copy code
Now we use it to generate extended array_square_sum. ($ represents prompt command) - $ ./ext_skel --extname=array_square_sum
- Creating directory array_square_sum
- Creating basic files: config.m4 config.w32 .svnignore array_square_sum.c php_array_square_sum.h CREDITS EXPERIMENTAL tests/001. phpt array_square_sum.php [done].
- To use your new extension, you will have to execute the following steps:
- 1. $ cd ..
- 2. $ vi ext/array_square_sum/config.m4
- 3. $ . /buildconf
- 4. $ ./configure --[with|enable]-array_square_sum
- 5. $ make
- 6. $ ./php -f ext/array_square_sum/array_square_sum.php
- 7. $ vi ext/array_square_sum/array_square_sum. c
- 8. $ make
- Repeat steps 3-6 until you are satisfied with ext/array_square_sum/config.m4 and
- step 6 confirms that your module is compiled into PHP. Then, start writing
- code and repeat the last two steps as often as necessary.
Copy the code After executing the command, the terminal tells us how to produce a new extension. Check the file content and you will find that there are several more important files: config.m4, php_array_square_sum.h, array_square_sum.c, which will be described one by one below. - liujun@ubuntu:~/test/php-5.5.8/ext$ ll array_square_sum/
- total 44
- drwxr-xr-x 3 liujun liujun 4096 Jan 29 15:40 ./
- drwxr-xr-x 80 liujun liujun 4096 Jan 29 15:40 ../
- -rw-r--r-- 1 liujun liujun 5548 Jan 29 15:40 array_square_sum.c
- -rw-r--r-- 1 liujun liujun 532 Jan 29 15:40 array_square_sum.php
- -rw-r--r-- 1 liujun liujun 2354 Jan 29 15:40 config.m4
- -rw-r--r-- 1 liujun liujun 366 Jan 29 15:40 config.w32
- -rw-r--r-- 1 liujun liujun 16 Jan 29 15:40 CREDITS
- -rw-r--r-- 1 liujun liujun 0 Jan 29 15:40 EXPERIMENTAL
- -rw-r--r-- 1 liujun liujun 3112 Jan 29 15:40 php_array_square_sum.h
- -rw-r--r-- 1 liujun liujun 16 Jan 29 15:40 .svnignore
- drwxr-xr-x 2 liujun liujun 4096 Jan 29 15:40 tests/
Copy code
3.2 Configuration file config.m4- dnl PHP_ARG_WITH(array_square_sum, for array_square_sum support,
- dnl Make sure that the comment is aligned:
- dnl [ --with-array_square_sum Include array_square_sum support])
Copy code
Remove dnl - PHP_ARG_WITH(array_square_sum, for array_square_sum support,
- Make sure that the comment is aligned:
- [ --with-array_square_sum Include array_square_sum support])
Copy code This is the minimum requirement to be able to call the enable-sample option when ./configure. The second parameter of PHP_ARG_ENABLE will be displayed when this extended configuration file is reached during ./configure processing. The third parameter will be executed by the end user. /configure --help is displayed as help information
3.3 Header file
Modify php_array_square_sum.h and change confirm_array_square_sum_compiled to confirm_array_square_sum. This is the name of the function we will actually call in the future. Of course, you can also directly add the function confirm_array_square_sum without deleting confirm_array_square_sum_compiled. - PHP_FUNCTION(confirm_array_square_sum_compiled);
Copy code It should be - PHP_FUNCTION(array_square_sum);
Copy code
3.3 Source code
Modify array_square_sum.c and change confirm_array_square_sum_compiled to confirm_array_square_sum. This is the function to register this extension. If confirm_array_square_sum was directly added in 3.2, just add confirm_array_square_sum directly in this step. - const zend_function_entry array_square_sum_functions[] = {
- PHP_FE(confirm_array_square_sum_compiled, NULL) /* For testing, remove later. */
- PHP_FE_END /* Must be the last line in array_square_sum_functions[] */
- };
Copy the code and change it to - const zend_function_entry array_square_sum_functions[] = {
- PHP_FE(array_square_sum, NULL) /* For testing, remove later. */
- PHP_FE_END /* Must be the last line in array_square_sum_functions[] * /
- };
Copy code
Then the most critical step is to rewrite confirm_array_square_sum. At this time, you only need to rewrite confirm_array_square_sum_compiled into confirm_array_square_sum (confirm_array_square_sum_compiled was not deleted in 3.1, so you need to add confirm_array_square_sum). - PHP_FUNCTION(confirm_array_square_sum_compiled)
Copy code
Rewritten as - PHP_FUNCTION(array_square_sum)
- {
- zval* array_data;
- HashTable *ht_data;
- int ret;
- char* key;
- uint index;
- zval **pdata;
- double sum = 0;
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array_data) == FAILURE) {
- return;
- }
- ht_data = Z_ARRVAL_P(array_data);
- zend_hash_internal_pointer_reset(ht_data);
- while ( HASH_KEY_NON_EXISTANT != (ret = zend_hash_get_current_key(ht_data, &key, &index, 0)) ) {
- ret = zend_hash_get_current_data(ht_data, &pdata);
-
- if( Z_TYPE_P(*pdata) == IS_LONG){
- sum += Z_LVAL_P(*pdata) * Z_LVAL_P( *pdata);
- }else {
- RETURN_FALSE;
- }
- zend_hash_move_forward(ht_data);
- }
- zend_hash_internal_pointer_end(Z_ARRVAL_P(array_data));
- RETVAL_DOUBLE(sum);
- }
Copy code
PHP is a weakly typed language, and its data is stored in the structure zval ( Please see more professional information for details, such as "php extension development.pdf"). - typedef union _zval {
- long lval;
- double dval;
- struct {
- char *val;
- int len;
- } str;
- HashTable *ht;
- zend_object_value obj;
- } zval;
Copy Code
In order to obtain the parameters passed by the function, you can use the zend_parse_parameters() API function. The following is the prototype of the function: - zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, …);
Copy code
We can directly generate the first few parameters of the zend_parse_parameters() function using macros in the kernel, in the form: ZEND_NUM_ARGS() TSRMLS_CC. Note that there is a space between the two, but there is no comma. As can be seen from the name, ZEND_NUM_ARGS() represents the number of parameters. Followed by common parameter types (similar to printf in C language), followed by common parameter lists.
The following table lists common parameter types.
Parameter type |
Object C type |
Instructions |
l |
long |
integer |
b |
bool |
Boolean |
s |
char* |
String |
d |
double |
Floating point numbers |
a |
array(zval*) |
Array |
z |
zval* |
Uncertainty zval |
In addition, the array is implemented as a large hashtable, so zend_hash_get_current_key can traverse the array and use the macro Z_LVAL_P(zval*) to obtain the actual value. Finally, the results can be put into sum. RETVAL_DOUBLE(value) is also a macro. The return result is double and the value is value. For details, please see "php extension development.pdf".
The development of this main function is finally completed.
3.4 Generate configure file
Then execute ~/php/bin/phpize
- /home/liujun/php/bin/phpize
Copy code- Configuring for:
- PHP Api Version: 20121113
- Zend Module Api No: 20121 212
- Zend Extension Api No: 220121212
Copy code
You can find that the executable script configure appears in array_square_sum.
3.5 Compilation
It is best to bring php-config PATH when compiling, because the system default php-config-path may not be the php path you are currently using. - liujun@ubuntu:~/test/php-5.5.8/ext/array_square_sum$ ./configure --with-php-config=/home/liujun/php/bin/php-config
Copy If the code is compiled successfully, the terminal will have the following prompt: - creating libtool
- appending configuration tag "CXX" to libtool
- configure: creating ./config.status
- config.status: creating config.h
- config .status: config.h is unchanged
Copy the code Check the module directory of the array_square_sum directory, and you will find that array_square_sum.so is generated in it. This is the extension we need. - liujun@ubuntu:~/test/php-5.5.8/ext/array_square_sum$ ls modules/
- array_square_sum.la array_square_sum.so
Copy code
4. Use extensions
4.1. Configuration extension
Modify the php configuration php.ini and add the configuration content. - [array_square_sum]
- extension=array_square_sum.so
Copy code
4.2. Add module
PHP extensions are generally in $PHP_PATH/lib/php/extensions/no-debug-non-zts-yyyymmdd. If you can’t find it, please go to Baidu or Google. There are many .so files in it.
Just copy array_sum_square.so produced in 3.5.
If you use fastcgi mode, you need to restart php, so our php should have the extension array_square_sum, You can check phpinfo for details (please use Baidu or Google if you don’t know).
4.2. Write code
Since writing extensions can improve operating efficiency, here we compare and test performance by using extensions and directly using PHP code. Experimenting multiple times can reduce errors, so do 2,000 times to find the sum of the squares of 100,000 numbers. The code is as follows: - $data = array();
- $max_index = 100000;
- $test_time = 2000;
- for($i=0; $i<$max_index; $i++){
- $data[] = $i;
- }
- $php_test_time_start = time();
- php_test($test_time, $data);
- $php_test_time_stop = time();
- echo "php test ext time is ". ($ php_test_time_stop - $php_test_time_start). "n";
- $c_test_time_start = time();
- c_test($test_time, $data);
- $c_test_time_stop = time();
- echo "php test time is ". ($c_test_time_stop - $c_test_time_start). "n";
-
- function php_test($test_time, $test_data){
- for($i=0; $i<$test_time; $i++){
- $sum = 0;
- foreach($test_data as $data){
- $sum += $data * $data;
- }
- }
- }
-
- function c_test($test_time, $test_data){
- for($i=0; $i<$test_time; $i++) {
- $sum = array_square_sum($test_data);
- }
- }
Copy code The test results are as follows: - liujun@ubuntu:~/php$ ~/php/bin/php test.php
- php test ext time is 30
- php test time is 2
Copy code You can see that the extension is 15 times faster than using php directly. As business logic becomes more complex, this differentiation will become greater.
Then use c language to do this directly. Here is a code to test (the test conditions are exactly the same): - #include
- #include
- #include
-
- #define TEST_TIME 2000
- #define MAX_INDEX 100000
-
- int main()
- {
- int data[MAX_INDEX];
- double sum = 0;
-
- for(int i=0; i
- data[i] = i ;
- }
-
- struct timeval start;
- struct timeval end;
-
- gettimeofday(&start,NULL);
-
- for(int i=0; i
- for(int j=0; j
- sum += data[j] * data[j];
- }
- }
- gettimeofday(&end,NULL);
-
- double time=(end.tv_sec-start.tv_sec) + (end.tv_usec- start.tv_usec) * 1.0 /1000000;
- printf("total time is %lfn", time );
- printf("sum time is %lfn", sum);
- return 0;
- }
Copy code
Execute and view the effect. It can be seen that the time of using C directly is only 0.261746, which is 13.09% of using C extension and 0.87% of using PHP directly. Of course, if complex operations such as IO are involved, C/C++ will be tens of thousands of times faster than PHP (tested).
- liujun@ubuntu:~/php$ g++ test.cpp -o test
- liujun@ubuntu:~/php$ ./test
- total time is 0.261746
- sum time is 36207007178615872. 000000
Copy code
Therefore, in actual services that require very high performance requirements, such as indexing, word segmentation, etc., you can use C to create a set of underlying services and use PHP to encapsulate the calls.
|