Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Advanced PHP Programming

.pdf
Скачиваний:
71
Добавлен:
14.04.2015
Размер:
7.82 Mб
Скачать

498 Chapter 20 PHP and Zend Engine Internals

The following are some important elements of this struct:

n module_startup_func—This hook is called when the module is first loaded.This traditionally registers globals, performs any one-time initializations, and registers any .ini file entries that the module wants to use. In some pre-fork architectures, notably Apache, this function is called in the parent process, before forking.This makes it an inappropriate place to initialize open sockets or database connections because they may not behave well if multiple processes try to use the same resources.

n module_shutdown_func—This hook is called when the interpreter shuts down. Any resources that the module has allocated should be freed here.

n request_startup_func—This is called at the beginning of each request.This hook is particularly useful for setting up any sort of per-request resources that a script may need.

nrequest_shutdown_func—This is called at the end of every request.

nfunctions—This is the function that the extension defines.

nini_functions—This is the .ini file entries that the extension registers.

The Zend Extension API

The final component of the PHP request life cycle is the extension API that the Zend Engine itself provides for extensibility.There are two major components of the extensibility: Certain key internal functions are accessed via function pointers, meaning that they can be overridden at runtime, and there is a hook API that allows an extension to register code to be run before certain opcodes.

These are the main function pointers used in the Zend Engine:

n zend_compile—We discussed this function at the beginning of the chapter. zend_compile is the wrapper for the lexer, parser, and code generator. APC and the other compiler caches overload this pointer so that they can return cached copies of scripts’ op arrays.

n zend_execute—Also discussed earlier in this chapter, this is the function that executes the code generated by zend_compile. APD and the other code profilers overload zend_execute so that they can track with high granularity the time spent in every function call.

n zend_error_cb—This is a pointer that sets the function called anytime an error is triggered in PHP. If you wanted to write an extension that automatically converts errors to exceptions, this would be the place to do it.

n zend_fopen—This is the function that implements the open call that is used internally whenever a file needs to be opened.

The PHP Request Life Cycle

499

The hook API is an extension of the PHP extension API:

struct _zend_extension { char *name;

char *version; char *author; char *URL;

char *copyright;

startup_func_t startup; shutdown_func_t shutdown; activate_func_t activate; deactivate_func_t deactivate; message_handler_func_t message_handler;

op_array_handler_func_t op_array_handler; statement_handler_func_t statement_handler; fcall_begin_handler_func_t fcall_begin_handler; fcall_end_handler_func_t fcall_end_handler; op_array_ctor_func_t op_array_ctor; op_array_dtor_func_t op_array_dtor;

int (*api_no_check)(int api_no); void *reserved2;

void *reserved3; void *reserved4; void *reserved5; void *reserved6; void *reserved7; void *reserved8; DL_HANDLE handle; int resource_number;

};

The pointers provide the following functionality:

n startup—This is functionally identical to an extension’s module_startup_func function.

n shutdown—This is functionally identical to an extension’s module_shutdown_func function.

n activate—This is functionally identical to an extension’s request_startup_func function.

ndeactivate—This is functionally identical to an extension’s request_shutdown_func function.

500 Chapter 20 PHP and Zend Engine Internals

nmessage_handler—This is called when the extension is registered.

nop_array_handler—This is called on a function’s op_array after the function is

compiled.

n statement_handler—If this handler is set, an additional opcode is inserted before every statement.This opcode’s handler executes all the registered statement handlers.This handler can be useful for debugging extensions, but because it effectively doubles the size of the script’s op array, it can have a deleterious effect on system performance.

nfcall_begin_handler—If this handler is set, an additional opcode is inserted before every ZEND_DO_FCALL and ZEND_DO_FCALL_BY_NAME opcode.That opcode’s

handler executes all registered fcall_begin_handler functions.

nfcall_end_handler—If this handler is set, an additional opcode is inserted after every ZEND_DO_FCALL and ZEND_DO_FCALL_BY_NAME opcode.That opcode’s han-

dler executes all registered fcall_end_handler functions.

How All the Pieces Fit Together

The preceding sections provide a lot of information. PHP, SAPIs, the Zend Engine— there are a lot of moving parts to consider.The most important part in understanding how a system works is understanding how all the pieces fit together. Each SAPI is unique in how it ties all the pieces together, but all the SAPIs follow the same basic pattern.

Figure 20.3 shows the complete life cycle of the mod_php5 SAPI. After the initial server startup, the process loops the handling requests.

TEAM

FLY

 

 

 

The PHP Request Life Cycle

501

 

 

 

 

 

 

 

 

 

 

 

 

 

Startup

Per Request Steps

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

run extension request

 

 

 

 

sapi_startup

 

 

 

 

php_request_startup

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

startup functions

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

php_module_startup

 

 

 

 

php_output_activate

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

php_output_startup

zend_startup

parse ini values

startup internal extensions

startup dynamically Ioaded extensions

startup zend_extensions

request end

sapi_shutdown

zend_shutdown

 

zend_activate

 

initialize compiler and executor

 

 

 

 

 

 

pull in request data

 

 

 

 

 

 

 

 

 

sapi_activate

 

 

 

from Apache

 

 

 

 

 

 

 

 

run zend_extension

 

 

 

 

 

zend_activate_modules

 

 

 

activate functions

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

zend_compile

 

 

 

 

 

 

 

 

 

actually parse and execute

 

 

 

 

 

 

 

 

 

zend_compile

 

the script

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

zend_shutdown_modules

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

zend_deactivate

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

sapi_deactivate

 

 

 

 

 

 

 

 

 

 

 

 

Figure 20.3 The mod_php5 request life cycle.

502 Chapter 20 PHP and Zend Engine Internals

Further Reading

Documentation for the Zend Engine is pretty scarce. If you prefer a more hands-on introduction than is presented here, skip ahead to Chapter 23, where you will see a complete walkthrough of the CGI SAPI as well as extensive coverage of how to embed PHP into external applications.

21

Extending PHP: Part I

UNDER THE HOOD, PHP INTERNAL FUNCTIONS and classes are all implemented in C. Developers can write PHP functions and classes in C or C++ as well.There are two major reasons you might want to write your own PHP extensions:

nInterfacing with an external library—If you have an external library you would like to have access to in PHP, the only real solution is to write an extension wrapper for it.You might want to do this for a library that you have developed inhouse, a library whose license precludes a wrapper library for it being included in the PHP distribution, or a library that simply hasn’t had a PHP interface released. In the latter case, the library may be an ideal candidate for inclusion in PHP via the PECL extension library in PEAR.

nPerformance—There may be critical portions of your business logic that you have been unable to optimize using the other techniques presented in this book up to now.The ultimate step in performance tuning is to convert your business logic to C. Because C functions do not execute on the Zend virtual machine, they have significantly less overhead. Function speedups of 10 to 100 times are reasonable to expect for functions that are not bound by external resources (database calls, remote data fetching, RPCs, and so on).

Although both of these reasons are strong, a general word of warning should be given for anyone considering writing an application, especially for performance reasons: One of the strengths of PHP is its shallow learning curve. One of the major benefits of using a high-level language (like PHP or Perl and unlike C or C++) is that it shields you from having to perform your own memory management and from making errors that can cause the PHP interpreter itself to crash.

When you write a C extension, you lose both of these benefits.When application logic becomes (even partially) implemented in C, you need a C programmer to maintain the application.This can be impractical for many smaller organizations (and even some larger ones) if staffing efforts are focused on having PHP programmers, not C

504 Chapter 21 Extending PHP: Part I

programmers. Just because you are proficient in C does not mean that your replacement will be. Although it’s possible to think of this as some sort of twisted job security, painting either yourself or your employer (who now has to staff C programmers, as well as PHP programmers) into a corner is something you should not do without considerable forethought.

In addition, C is more difficult to program well than PHP. Because data created in extensions is not magically handled by the Zend garbage-collection system, you have to be careful not to leak memory or resources; the Zend API in particular approaches black magic when it comes to handling resource references in extensions.The C debugging process is much longer than the PHP debugging process:You cannot simply change a line of code and have it take effect; you must make the change, recompile, and restart the application for the change to take effect.You also expose yourself to application crashes (segmentation faults, and so on) if you perform actions you shouldn’t in C.

Like almost every potential performance optimization, retooling an application in C is a matter of trade-offs.With C, these are the benefits:

nSpeed

nReduced complexity of the PHP code

These are the drawbacks:

nReduced maintainability

nLengthened development cycle

nIncreased brittleness of the application

For some organizations, these trade-offs make sense. Also, if you are trying to interface with an external library, there is usually no choice but to provide access via a wrapper extension.

Extension Basics

If you know C, writing PHP extensions is not terribly difficult. PHP provides a number of tools that make bridging PHP and C code easy.This section provides all the steps necessary to build a PHP extension that registers procedural functions.

Creating an Extension Stub

The easiest way to create a new extension is to use a default extension skeleton.You do this with the ext_skel script in the ext directory of the PHP source tree.To create an extension named example, you would use the following from the root of the source tree:

>cd ext

>./ext_skel --extname=example Creating directory example

Extension Basics

505

Creating basic files: config.m4 .cvsignore example.c

php_example.h CREDITS EXPERIMENTAL tests/001.phpt example.php

[done].

To use your new extension, you have to execute the following:

1.$ cd ..

2.$ vi ext/example/config.m4

3.$ ./buildconf

4.$ ./configure --[with|enable]-example

5.$ make

6.$ ./php -f ext/example/example.php

7.$ vi ext/example/example.c

8.$ make

Repeat steps 3-6 until you are satisfied with ext/example/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.

This code creates a directory named example with all the files necessary to build the extension.The first file of importance is example.c; it is the master C source file for the extension. It looks like the following (from which I’ve trimmed some nonessential parts for readability):

#ifdef HAVE_CONFIG_H #include config.h#endif

#include php.h#include php_ini.h

#include ext/standard/info.h#include php_example.h

#define VERSION 1.0

function_entry example_functions[] = { {NULL, NULL, NULL}

};

zend_module_entry example_module_entry = { STANDARD_MODULE_HEADER,

example,

example_functions,

PHP_MINIT(example),

PHP_MSHUTDOWN(example),

PHP_RINIT(example),

PHP_RSHUTDOWN(example),

PHP_MINFO(example),

506 Chapter 21 Extending PHP: Part I

VERSION,

STANDARD_MODULE_PROPERTIES

};

#ifdef COMPILE_DL_EXAMPLE ZEND_GET_MODULE(example) #endif

PHP_MINIT_FUNCTION(example)

{

return SUCCESS;

PHP_MSHUTDOWN_FUNCTION(example)

{

return SUCCESS;

}

PHP_RINIT_FUNCTION(example)

{

return SUCCESS;

}

PHP_RSHUTDOWN_FUNCTION(example)

{

return SUCCESS;

}

PHP_MINFO_FUNCTION(example)

{

php_info_print_table_start();

php_info_print_table_header(2, example support, enabled); php_info_print_table_end();

}

Later sections of this chapter discuss the meanings of the parts of this code.

The next file to inspect is the config.m4 file.This is a set of m4 macros that specify the build-time flags for the extension.The following is a simple .m4 script that requires you to specify --enable-example to build the extension:

PHP_ARG_ENABLE(example,

to enable the example extension,

[ --enable-example

enable the example extension.])

if test $PHP_EXAMPLE!= no; then PHP_NEW_EXTENSION(example, example.c, $ext_shared)

fi

Extension Basics

507

The PHP build system supports the full .m4 syntax set, as well as some custom macros. Here is a partial list of the custom PHP build system macros:

n PHP_CHECK_LIBRARY(library, func [, found [, not-found [,

extra-libs]]])—Checks for the existence of the function func in the library. If the function exists, this macro evaluates to found; otherwise, it evaluates to notfound. extra_libs specifies additional libraries to add to the lib line.

n PHP_DEFINE(what, [value])—Acts as a basic wrapper around AC_DEFUN and sets the necessary code to add the following:

#define what value

n PHP_ADD_SOURCES(path, sources[, special-flags[, type]])—Adds additional sources from path to the build. If you split extension sources across multiple files, this macro allows you to automatically build and link them all.

n PHP_ADD_LIBRARY(library[, append[, shared-libadd]])—Adds library to the

link line.

n PHP_ADD_INCLUDE(path [,before])—Adds path to the build line. If before is set, prepend it to the include path. Otherwise, append it to the include path.

The full set of custom .m4 macros is in the file acinclude.m4 in the top level of the PHP source tree.

These are the other files created by ext_skel:

nCREDITS—This file is not necessary but is nice if you distribute an extension.

nEXPERIMENTAL—This flag file marks the extension as experimental.This is useful

only if the extension is bundled with PHP itself.

nexample.php—This is a sample script that loads and uses the extension.

nphp_example.h—This is a default header file for the extension.

ntests/001.phpt—This is a unit test that uses the PHP build system unit-testing

suite.Testing is good.

Building and Enabling Extensions

After an extension is authored, there are two ways to build it: statically or dynamically. A static extension is built into PHP when PHP itself is compiled, and a dynamic extension can be built at any time and is specified to be loaded in the php.ini file.

To build a static extension, the sources must be in a directory under ext/ in the PHP build tree.Then, from the root of the tree, you run this:

>./buildconf

This reconfigures the PHP build system and adds the configuration options to the main configuration script.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]