Home  >  Article  >  Backend Development  >  Step-by-step guide to writing PHP extensions

Step-by-step guide to writing PHP extensions

WBOY
WBOYOriginal
2016-07-25 09:12:32937browse

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.

  1. function array_square_sum($data){
  2. $sum = 0;
  3. foreach($data as $value){
  4. $sum += $value * $value;
  5. }
  6. return $sum ;
  7. }
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.

  1. liujun@ubuntu:~/test/php-5.5.8/ext$ ls ext_skel
  2. ext_skel
Copy code

Now we use it to generate extended array_square_sum. ($ represents prompt command)

  1. $ ./ext_skel --extname=array_square_sum
  2. Creating directory array_square_sum
  3. 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].
  4. To use your new extension, you will have to execute the following steps:
  5. 1. $ cd ..
  6. 2. $ vi ext/array_square_sum/config.m4
  7. 3. $ . /buildconf
  8. 4. $ ./configure --[with|enable]-array_square_sum
  9. 5. $ make
  10. 6. $ ./php -f ext/array_square_sum/array_square_sum.php
  11. 7. $ vi ext/array_square_sum/array_square_sum. c
  12. 8. $ make
  13. Repeat steps 3-6 until you are satisfied with ext/array_square_sum/config.m4 and
  14. step 6 confirms that your module is compiled into PHP. Then, start writing
  15. 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.

  1. liujun@ubuntu:~/test/php-5.5.8/ext$ ll array_square_sum/
  2. total 44
  3. drwxr-xr-x 3 liujun liujun 4096 Jan 29 15:40 ./
  4. drwxr-xr-x 80 liujun liujun 4096 Jan 29 15:40 ../
  5. -rw-r--r-- 1 liujun liujun 5548 Jan 29 15:40 array_square_sum.c
  6. -rw-r--r-- 1 liujun liujun 532 Jan 29 15:40 array_square_sum.php
  7. -rw-r--r-- 1 liujun liujun 2354 Jan 29 15:40 config.m4
  8. -rw-r--r-- 1 liujun liujun 366 Jan 29 15:40 config.w32
  9. -rw-r--r-- 1 liujun liujun 16 Jan 29 15:40 CREDITS
  10. -rw-r--r-- 1 liujun liujun 0 Jan 29 15:40 EXPERIMENTAL
  11. -rw-r--r-- 1 liujun liujun 3112 Jan 29 15:40 php_array_square_sum.h
  12. -rw-r--r-- 1 liujun liujun 16 Jan 29 15:40 .svnignore
  13. drwxr-xr-x 2 liujun liujun 4096 Jan 29 15:40 tests/
Copy code

3.2 Configuration file config.m4

  1. dnl PHP_ARG_WITH(array_square_sum, for array_square_sum support,
  2. dnl Make sure that the comment is aligned:
  3. dnl [ --with-array_square_sum Include array_square_sum support])
Copy code

Remove dnl

  1. PHP_ARG_WITH(array_square_sum, for array_square_sum support,
  2. Make sure that the comment is aligned:
  3. [ --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.

  1. PHP_FUNCTION(confirm_array_square_sum_compiled);
Copy code

It should be

  1. 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.

  1. const zend_function_entry array_square_sum_functions[] = {
  2. PHP_FE(confirm_array_square_sum_compiled, NULL) /* For testing, remove later. */
  3. PHP_FE_END /* Must be the last line in array_square_sum_functions[] */
  4. };
Copy the code

and change it to

  1. const zend_function_entry array_square_sum_functions[] = {
  2. PHP_FE(array_square_sum, NULL) /* For testing, remove later. */
  3. PHP_FE_END /* Must be the last line in array_square_sum_functions[] * /
  4. };
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).

  1. PHP_FUNCTION(confirm_array_square_sum_compiled)
Copy code

Rewritten as

  1. PHP_FUNCTION(array_square_sum)
  2. {
  3. zval* array_data;
  4. HashTable *ht_data;
  5. int ret;
  6. char* key;
  7. uint index;
  8. zval **pdata;
  9. double sum = 0;
  10. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array_data) == FAILURE) {
  11. return;
  12. }
  13. ht_data = Z_ARRVAL_P(array_data);
  14. zend_hash_internal_pointer_reset(ht_data);
  15. while ( HASH_KEY_NON_EXISTANT != (ret = zend_hash_get_current_key(ht_data, &key, &index, 0)) ) {
  16. ret = zend_hash_get_current_data(ht_data, &pdata);
  17. if( Z_TYPE_P(*pdata) == IS_LONG){
  18. sum += Z_LVAL_P(*pdata) * Z_LVAL_P( *pdata);
  19. }else {
  20. RETURN_FALSE;
  21. }
  22. zend_hash_move_forward(ht_data);
  23. }
  24. zend_hash_internal_pointer_end(Z_ARRVAL_P(array_data));
  25. RETVAL_DOUBLE(sum);
  26. }
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").

  1. typedef union _zval {
  2. long lval;
  3. double dval;
  4. struct {
  5. char *val;
  6. int len;
  7. } str;
  8. HashTable *ht;
  9. zend_object_value obj;
  10. } 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:

  1. 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

  1. /home/liujun/php/bin/phpize
Copy code

  1. Configuring for:
  2. PHP Api Version: 20121113
  3. Zend Module Api No: 20121 212
  4. 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.

  1. 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:

  1. creating libtool
  2. appending configuration tag "CXX" to libtool
  3. configure: creating ./config.status
  4. config.status: creating config.h
  5. 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.

  1. liujun@ubuntu:~/test/php-5.5.8/ext/array_square_sum$ ls modules/
  2. 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.

  1. [array_square_sum]
  2. 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:

  1. $data = array();
  2. $max_index = 100000;
  3. $test_time = 2000;
  4. for($i=0; $i<$max_index; $i++){
  5. $data[] = $i;
  6. }
  7. $php_test_time_start = time();
  8. php_test($test_time, $data);
  9. $php_test_time_stop = time();
  10. echo "php test ext time is ". ($ php_test_time_stop - $php_test_time_start). "n";
  11. $c_test_time_start = time();
  12. c_test($test_time, $data);
  13. $c_test_time_stop = time();
  14. echo "php test time is ". ($c_test_time_stop - $c_test_time_start). "n";
  15. function php_test($test_time, $test_data){
  16. for($i=0; $i<$test_time; $i++){
  17. $sum = 0;
  18. foreach($test_data as $data){
  19. $sum += $data * $data;
  20. }
  21. }
  22. }
  23. function c_test($test_time, $test_data){
  24. for($i=0; $i<$test_time; $i++) {
  25. $sum = array_square_sum($test_data);
  26. }
  27. }
Copy code

The test results are as follows:

  1. liujun@ubuntu:~/php$ ~/php/bin/php test.php
  2. php test ext time is 30
  3. 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):

  1. #include
  2. #include
  3. #include
  4. #define TEST_TIME 2000
  5. #define MAX_INDEX 100000
  6. int main()
  7. {
  8. int data[MAX_INDEX];
  9. double sum = 0;
  10. for(int i=0; i
  11. data[i] = i ;
  12. }
  13. struct timeval start;
  14. struct timeval end;
  15. gettimeofday(&start,NULL);
  16. for(int i=0; i
  17. for(int j=0; j
  18. sum += data[j] * data[j];
  19. }
  20. }
  21. gettimeofday(&end,NULL);
  22. double time=(end.tv_sec-start.tv_sec) + (end.tv_usec- start.tv_usec) * 1.0 /1000000;
  23. printf("total time is %lfn", time );
  24. printf("sum time is %lfn", sum);
  25. return 0;
  26. }
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).

  1. liujun@ubuntu:~/php$ g++ test.cpp -o test
  2. liujun@ubuntu:~/php$ ./test
  3. total time is 0.261746
  4. 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.



Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn