Linux下用php调用C接口(扩展)的资料

linux开发php c扩展实例 php取得成功的一个主要原因之一是它拥有大量的可用扩展,web开发者无论有何种需求,这种需求最有可能在php发行包里找到 php发行包包括支持各种数据库,图形文件格式,压缩,XML技术扩展在内的许多扩展 PHP扩展(PECL)跟php引擎一样都是使用c语言开发 php扩展开发需要注意什么? 1、开发扩展比使用PHP开发相同功能成本与难度都要更高 2、后期维护不易 3、系统及PH 阅读详情

http://www.zend.com/php/internals/extension-writing1.php

By Sara Golemon

March 1, 2005

Introduction
What's an Extension?
Lifecycles
Memory Allocation
Setting Up a Build Environment
Hello World
Building Your Extension
INI Settings
Global Values
INI Settings as Global Values
Sanity Check
What's Next?

Introduction

If you're reading this tutorial, you probably have some interest in writing an extension for the PHP language. If not... well perhaps when we're done you'll have discovered an interest you didn't know existed!

This tutorial assumes basic familiarity with both the PHP language and the language the PHP interpreter is written in: C.

Let's start by identifying why you might want to write a PHP extension.

  1. There is some library or OS specific call which cannot be made from PHP directly because of the degree of abstraction inherent in the language.
  2. You want to make PHP itself behave in some unusual way.
  3. You've already got some PHP code written, but you know it could be faster, smaller, and consume less memory while running.
  4. You have a particularly clever bit of code you want to sell, and it's important that the party you sell it to be able to execute it, but not view the source.

These are all perfectly valid reasons, but in order to create an extension, you need to understand what an extension is first.

What's an Extension?

If you've used PHP, you've used extensions. With only a few exceptions, every userspace function in the PHP language is grouped into one extension or another. A great many of these functions are part of the standard extension - over 400 of them in total. The PHP source bundle comes with around 86 extensions, having an average of about 30 functions each. Do the math, that's about 2500 functions. As if this weren't enough, the PECL repository offers over 100 additional extensions, and even more can be found elsewhere on the Internet.

"With all these functions living in extensions, what's left?" I hear you ask. "What are they an extension to? What is the 'core' of PHP?"

PHP's core is made up of two separate pieces. At the lowest levels you find the Zend Engine (ZE). ZE handles parsing a human-readable script into machine-readable tokens, and then executing those tokens within a process space. ZE also handles memory management, variable scope, and dispatching function calls. The other half of this split personality is the PHP core. PHP handles communication with, and bindings to, the SAPI layer (Server Application Programming Interface, also commonly used to refer to the host environment – Apache, IIS, CLI, CGI, etc). It also provides a unified control layer for safe_mode and open_basedir checks, as well as the streams layer which associates file and network I/O with userspace functions like fopen(), fread(), and fwrite().

Lifecycles

When a given SAPI starts up, for example in response to /usr/local/apache/bin/apachectl start, PHP begins by initializing its core subsystems. Towards the end of this startup routine, it loads the code for each extension and calls their Module Initialization routine (MINIT). This gives each extension a chance to initialize internal variables, allocate resources, register resource handlers, and register its functions with ZE, so that if a script calls one of those functions, ZE knows which code to execute.

Next, PHP waits for the SAPI layer to request a page to be processed. In the case of the CGI or CLI SAPIs, this happens immediately and only once. In the case of Apache, IIS, or other fully-fledged web server SAPIs, it occurs as pages are requested by remote users and repeats any number of times, possibly concurrently. No matter how the request comes in, PHP begins by asking ZE to setup an environment for the script to run in, then calls each extension's Request Initialization (RINIT) function. RINIT gives the extension a chance to set up specific environment variables, allocate request specific resources, or perform other tasks such as auditing. A prime example of the RINIT function in action is in the sessions extension where, if the session.auto_start option is enabled, RINIT will automatically trigger the userspace session_start() function and pre-populate the $_SESSION variable.

Once the request is initialized, ZE takes over by translating the PHP script into tokens, and finally to opcodes which it can step through and execute. Should one of these opcodes require an extension function to be called, ZE will bundle up the arguments for that function, and temporarily give over control until it completes.

After a script has finished executing, PHP calls the Request Shutdown (RSHUTDOWN) function of each extension to perform any last minute cleanup (such as saving session variables to disk). Next, ZE performs a cleanup process (known as garbage collection) which effectively performs an unset() on every variable used during the previous request.

Once completed, PHP waits for the SAPI to either request another document or signal a shutdown. In the case of the CGI and CLI SAPIs, there is no "next request", so the SAPI initiates a shutdown immediately. During shutdown, PHP again cycles through each extension calling their Module Shutdown (MSHUTDOWN) functions, and finally shuts down its own core subsystems.

This process may sound daunting at first, but once you dive into a working extension it should all gradually start to make sense.

Memory Allocation

In order to avoid losing memory to poorly written extensions, ZE performs its own internal memory management using an additional flag that indicates persistence. A persistent allocation is a memory allocation that is meant to last for longer than a single page request. A non-persistent allocation, by contrast, is freed at the end of the request in which it was allocated, whether or not the free function is called. Userspace variables, for example, are allocated non-persistently because at the end of a request they're no longer useful.

While an extension may, in theory, rely on ZE to free non-persistent memory automatically at the end of each page request, this is not recommended. Memory allocations will remain unreclaimed for longer periods of time, resources associated with that memory will be less likely to be shutdown properly, and it's just poor practice to make a mess without cleaning it up. As you'll discover later on, it's actually quite easy to ensure that all allocated data is cleaned up properly

Let's briefly compare traditional memory allocation functions (which should only be used when working with external libraries) with persistent and non-persistent memory allocation within PHP/ZE.

TraditionalNon-PersistentPersistent
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)

* 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

Setting Up a Build Environment

Now that you've covered some of the theory behind the workings of PHP and the Zend Engine, I'll bet you'd like to dive in and start building something. Before you can do that however, you'll need to collect some necessary build tools and set up an environment suited to your purposes.

First you'll need PHP itself, and the set of build tools required by PHP. If you're unfamiliar with building PHP from source, I suggest you take a look at http://www.php.net/install.unix. (Developing PHP extensions for Windows will be covered in a later article). While it might be tempting to use a binary package of PHP from your distribution of choice, these versions tend to leave out two important ./configure options that are very handy during the development process. The first is --enable-debug. This option will compile PHP with additional symbol information loaded into the executable so that, if a segfault occurs, you'll be able to collect a core dump from it and use gdb to track down where the segfault occurred and why. The other option depends on which version of PHP you'll be developing against. In PHP 4.3 this option is named --enable-experimental-zts, in PHP 5 and later it's --enable-maintainer-zts. This option will make PHP think its operating in a multi-threaded environment and will allow you to catch common programming mistakes which, while harmless in a non-threaded environment, will cause your extension to be unusable in a multi-threaded one. Once you've compiled PHP using these extra options and installed it on your development server (or workstation), you can begin to put together your first extension.

Hello World

What programming introduction would be complete without the requisite Hello World application? In this case, you'll be making an extension that exports a single function returning a string containing the words: "Hello World". In PHP code you'd probably do it something like this:

<?php

function hello_world() {
    return
'Hello World';
}

?>

Now you're going to turn that into a PHP extension. First let's create a directory called hello under the ext/ directory in your PHP source tree and chdir into that folder. This directory can actually live anywhere inside or outside the PHP tree, but I'd like you to place it here to demonstrate an unrelated concept in a later article. Here you need to create three files: a source file containing your hello_world function, a header file containing references used by PHP to load your extension, and a configuration file used by phpize to prepare your extension for compiling.

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

#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);
}

Most of the code you can see in the example extension above is just glue - protocol language to introduce the extension to PHP and establish a dialogue for them to communicate. Only the last four lines are what you might call "real code" which performs a task on a level that the userspace script might interact with. Indeed the code at this level looks very similar to the PHP code we looked at earlier and can be easily parsed on sight:

  1. Declare a function named hello_world
  2. Have that function return a string: "Hello World"
  3. ....um.... 1? What's that 1 all about?

Recall that ZE includes a sophisticated memory management layer which ensures that allocated resources are freed when the script exits. In the land of memory management however, it's a big no-no to free the same block of memory twice. This action, called double freeing, is a common cause of segmentation faults, as it involves the calling program trying to access a block of memory which it no longer owns. Similarly, you don't want to allow ZE to free a static string buffer (such as "Hello World" in our example extension) as it lives in program space and thus isn't a data block to be owned by any one process. RETURN_STRING() could assume that any strings passed to it need to be copied so that they can be safely freed later; but since it's not uncommon for an internal function to allocate memory for a string, fill it dynamically, then return it, RETURN_STRING() allows us to specify whether it's necessary to make a copy of the string value or not. To further illustrate this concept, the following code snippet is identical to its counterpart above:

PHP_FUNCTION(hello_world)
{
    char *str;

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

In this version, you manually allocated the memory for the "Hello World" string that will ultimately be passed back to the calling script, then "gave" that memory to RETURN_STRING(), using a value of 0 in the second parameter to indicate that it didn't need to make its own copy, it could have ours.

Building Your Extension

The final step in this exercise will be building your extension as a dynamically loadable module. If you've copied the example above correctly, this should only take three commands run from ext/hello/:

$ phpize
$ ./configure --enable-hello
$ make

After running each of these commands, you should have a hello.so file in ext/hello/modules/ . Now, as with any other PHP extension, you can just copy this to your extensions directory (/usr/local/lib/php/extensions/ is the default, check your php.ini to be sure) and add the line extension=hello.so to your php.ini to trigger it to load on startup. For CGI/CLI SAPIs, this simply means the next time PHP is run; for web server SAPIs like Apache, this will be the next time the web server is restarted. Let's give it a try from the command line for now:

$ php -r 'echo hello_world();'

If everything's gone as it should, you should see Hello World output by this script, since the hello_world() function in your loaded extension returns that string, and the echo command displays whatever is passed to it (the result of the function in this case).

Other scalars may be returned in a similar fashion, using RETURN_LONG() for integer values, RETURN_DOUBLE() for floating point values, RETURN_BOOL() for true/false values, and RETURN_NULL() for, you guessed it, NULL values. Let's take a look at each of those in action by adding PHP_FE() lines to the function_entry struct in hello.c and adding some PHP_FUNCTION()s to the end of the file.

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();
}

You'll also need to add prototypes for these functions alongside the prototype for hello_world() in the header file, php_hello.h, so that the build process takes place properly:

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

Since you made no changes to the config.m4 file, it's technically safe to skip the phpize and ./configure steps this time and jump straight to make. However, at this stage of the game I'm going to ask you to go through all three build steps again just to make sure you have a nice build. In additional, you should call make clean all rather than simply make in the last step, to ensure that all source files are rebuilt. Again, this isn't necessary because of the types of changes you've made so far, but better safe than confused. Once the module is built, you'll again copy it to your extension directory, replacing the old version.

At this point you could call the PHP interpreter again, passing it simple scripts to test out the functions you just added. In fact, why don't you do that now? I'll wait here...

Done? Good. If you used var_dump() rather than echo to view the output of each function then you probably noticed that hello_bool() returned true. That's what the 1 value in RETURN_BOOL() represents. Just like in PHP scripts, an integer value of 0 equates to FALSE, while any other integer value equates to TRUE. Extension authors often use 1 as a matter of convention, and you're encouraged to do the same, but don't feel locked into it. For added readability, the RETURN_TRUE and RETURN_FALSE macros are also available; here's hello_bool() again, this time using RETURN_TRUE:

PHP_FUNCTION(hello_bool)
{
    RETURN_TRUE;
}

Note that no parentheses were used here. RETURN_TRUE and RETURN_FALSE are aberrations from the rest of the RETURN_*() macros in that way, so be sure not to get caught by this one!

You probably noticed in each of the code samples above that we didn't pass a zero or one value indicating whether or not the value should be copied. This is because no additional memory (beyond the variable container itself – we'll delve into this deeper in Part 2) needs to be allocated - or freed - for simple small scalars such as these.

There are an additional three return types: RESOURCE (as returned by mysql_connect(), fsockopen(), and ftp_connect() to name but a few), ARRAY (also known as a HASH), and OBJECT (as returned by the keyword new). We'll look at these in Part II of this series, when we cover variables in depth.

INI Settings

The Zend Engine provides two approaches for managing INI values. We'll take a look at the simpler approach for now, and explore the fuller, but more complex, approach later on, when you've had a chance to work with global values.

Let's say you want to define a php.ini value for your extension, hello.greeting, which will hold the value used to say hello in your hello_world() function. You'll need to make a few additions to hello.c and php_hello.h while making a few key changes to the hello_module_entry structure. Start off by adding the following prototypes near the userspace function prototypes in 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);

Now head over to hello.c and take out the current version of hello_module_entry, replacing it with the following listing:

zend_module_entry hell_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;
}

Now, you just need to add an #include to the rest of the #includes at the top of hello.c to get the right headers for INI file support:

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

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

Finally, you can modify your hello_world function to use the INI value:

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

Notice that you're copying the value returned by INI_STR(). This is because, as far as the PHP variable stack is concerned, this is a static string. In fact, if you tried to modify the string returned by this value, the PHP execution environment would become unstable and might even crash.

The first set of changes in this section introduced two methods you'll want to become very familiar with: MINIT, and MSHUTDOWN. As mentioned earlier, these methods are called during the initial startup of the SAPI layer and during its final shutdown, respectively. They are not called between or during requests. In this example you've used them to register the php.ini entries defined in your extension. Later in this series, you'll find how to use the MINIT and MSHUTDOWN functions to register resource, object, and stream handlers as well.

In your hello_world() function you used INI_STR() to retrieve the current value of the hello.greeting entry as a string. A host of other functions exist for retrieving values as longs, doubles, and Booleans as shown in the following table, along with a complementary ORIG counterpart which provides the value of the referenced INI setting as it was set in php.ini (before being altered by .htaccess or ini_set() statements).

Current ValueOriginal ValueType
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

The first parameter passed to PHP_INI_ENTRY() is a string containing the name of the entry to be used in php.ini. In order to avoid namespace collisions, you should use the same conventions as with your functions; that is, prefix all values with the name of your extension, as you did with hello.greeting. As a matter of convention, a period is used to separate the extension name from the more descriptive part of the ini setting name.

The second parameter is the initial value, and is always given as a char* string regardless of whether it is a numerical value or not. This is due primarily to the fact that values in an .ini file are inherently textual - being a text file and all. Your use of INI_INT(), INI_FLT(), or INI_BOOL() later in your script will handle type conversions.

The third value you pass is an access mode modifier. This is a bitmask field which determines when and where this INI value should be modifiable. For some, such as register_globals, it simply doesn't make sense to allow the value to be changed from within a script using ini_set() because the setting only has meaning during request startup - before the script has had a chance to run. Others, such as allow_url_fopen, are administrative settings which you don't want to allow users on a shared hosting environment to change, either via ini_set() or through the use of .htaccess directives. A typical value for this parameter might be PHP_INI_ALL, indicating that the value may be changed anywhere. Then there's PHP_INI_SYSTEM|PHP_INI_PERDIR, indicating that the setting may be changed in the php.ini file, or via an Apache directive in a .htaccess file, but not through the use of ini_set(). Or there's PHP_INI_SYSTEM, meaning that the value may only be changed in the php.ini file and nowhere else.

We'll skip the fourth parameter for now and only mention that it allows the use of a callback method to be triggered whenever the ini setting is changed, such as with ini_set(). This allows an extension to perform more precise control over when a setting may be changed, or trigger a related action dependant on the new setting.

Global Values

Frequently, an extension will need to track a value through a particular request, keeping that value independent from other requests which may be occurring at the same time. In a non-threaded SAPI that might be simple: just declare a global variable in the source file and access it as needed. The trouble is, since PHP is designed to run on threaded web servers (such as Apache 2 and IIS), it needs to keep the global values used by one thread separate from the global values used by another. PHP greatly simplifies this by using the TSRM (Thread Safe Resource Management) abstraction layer, sometimes referred to as ZTS (Zend Thread Safety). In fact, by this point you've already used parts of TSRM and didn't even know it. (Don't search too hard just yet; as this series progresses you'll come to discover it's hiding everywhere.)

The first part of creating a thread safe global is, as with any global, declaring it. For the sake of this example, you'll declare one global value which will start out as a long with a value of 0. Each time the hello_long() function is called you'll increment this value and return it. Add the following block of code to php_hello.h just after the #define PHP_HELLO_H statement:

#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

You're also going to use the RINIT method this time around, so you need to declare its prototype in the header:

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

Now let's go over to hello.c and add the following just after your include block:

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

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

ZEND_DECLARE_MODULE_GLOBALS(hello)

Change hello_module_entry by adding 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
};

And modify your MINIT function, along with the addition of another couple of functions, to handle initialization upon request startup:

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;
}

Finally, you can modify the hello_long() function to use this value:

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

    RETURN_LONG(HELLO_G(counter));
}

In your additions to php_hello.h, you used a pair of macros - ZEND_BEGIN_MODULE_GLOBALS() and ZEND_END_MODULE_GLOBALS() - to create a struct named zend_hello_globals containing one variable of type long. You then conditionally defined HELLO_G() to either fetch this value from a thread pool, or just grab it from a global scope - if you're compiling for a non-threaded environment.

In hello.c you used the ZEND_DECLARE_MODULE_GLOBALS() macro to actually instantiate the zend_hello_globals struct either as a true global (if this is a non-thread-safe build), or as a member of this thread's resource pool. As extension authors, this distinction is one we don't need to worry about, as the Zend Engine takes care of the job for us. Finally, in MINIT, you used ZEND_INIT_MODULE_GLOBALS() to allocate a thread safe resource id - don't worry about what that is for now.

You may have noticed that php_hello_init_globals() doesn't actually do anything, yet we went to the trouble of declaring RINIT to initialize the counter to 0. Why?

The key lies in when the two functions are called. php_hello_init_globals() is only called when a new process or thread is started; however, each process can serve more than one request, so using this function to initialize our counter to 0 will only work for the first page request. Subsequent page requests to the same process will still have the old counter value stored here, and hence will not start counting from 0. To initialize the counter to 0 for every single page request, we implemented the RINIT function, which as you learned earlier is called prior to every page request. We included the php_hello_init_globals() function at this point because you'll be using it in a few moments, but also because passing a NULL to ZEND_INIT_MODULE_GLOBALS() for the init function will result in a segfault on non-threaded platforms.

INI Settings as Global Values

If you recall from earlier, a php.ini value declared with PHP_INI_ENTRY() is parsed as a string value and converted, as needed, to other formats with INI_INT(), INI_FLT(), and INI_BOOL(). For some settings, that represents a fair amount of unnecessary work duplication as the value is read over and over again during the course of a script's execution. Fortunately it's possible to instruct ZE to store the INI value in a particular data type, and only perform type conversions when its value is changed. Let's try that out by declaring another INI value, a Boolean this time, indicating whether the counter should increment, or decrement. Begin by changing the MODULE_GLOBALS block in php_hello.h to the following:

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

Next, declare the INI value itself by changing your PHP_INI_BEGIN() block thus:

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()

Now initialize the setting in the init_globals method with:

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

And lastly, use the value of the ini setting in hello_long() to determine whether to increment or decrement:

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

    RETURN_LONG(HELLO_G(counter));
}

And that's it. The OnUpdateBool method you specified in the INI_ENTRY section will automatically convert any value provided in php.ini, .htaccess, or within a script via ini_set() to an appropriate TRUE/FALSE value which you can then access directly within a script. The last three parameters of STD_PHP_INI_ENTRY tell PHP which global variable to change, what the structure of our extension globals looks like, and the name of the global scope container where they're contained.

Sanity Check

By now our three files should look similar to the following listings. (A few items have been moved and grouped together, for the sake of readability.)

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->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();
}

What's Next?

In this tutorial we explored the structure of a simple PHP extension which exported functions, returned values, declared INI settings, and tracked its internal state during the course of a request.

In the next session we'll explore the internal structure of PHP variables, and how they're stored, tracked, and maintained within a script environment. We'll use zend_parse_parameters to receive parameters from a program when a function is called, and explore ways to return more complicated results, including the array, object, and resource types mentioned in this tutorial.


Copyright Sara Golemon, 2005. All rights reserved.



Readers' Comments

Begin a new thread

+ Expand tree

  See also PECL_Gen
Philip Olson
02/03/2005 17:21
  Hm
Tim Rieder
03/03/2005 23:21
  Inter-module communication
uttam
04/03/2005 15:54
  ext_skel
Tarique Sani
10/03/2005 10:03
  Very nice article
Peter Albertsson
19/03/2005 00:32
  Extensions under RHEL3/4, et al
Dave Nuttall
24/03/2005 13:05
  PHPize "no quotes" errors
Dave Nuttall
24/03/2005 13:18
  Problem Compiling...
Markus Duft
28/03/2005 17:42
  php-config: command not found
Danilo
26/04/2005 17:11
  Using this with -z?
Francisco Tolmasky
30/05/2005 11:25

在Linux下用C扩展PHP(打包成so)的方法 本文主要讲一下在Linux下用打包C扩展程序.so文件和Windows下的不同,详细的代码和配置方案请参加另一篇博客:http://blog.youkuaiyun.com/maverick1990/article/details/46519045 步骤: 1.安装php环境到目录 /usr/local/php/ 目录下 2.下载相同版本的php源码包,安装到 /root/php-5.6. 阅读详情

相关推荐

linux php调用c库

python中可以调用c代码,php也是可以的。c语言写的代码,不能直接在php中使用,不过可以用php扩展的方式调用。首先在php源码ext/下新建一个子目录example,然后创建config.m4config.m4: PHP_ARG_ENABLE(risk, [Whether to enable the "risk" extension], [ enable-risk

邹春旭的博客 698

PHP调用C语言实现接口方法

环境准备 环境: CentOS Linux release 7.3.1611 (Core) PHP 5.4.16 安装php、查看php版本 # yum install php php-devel # php -v so 动态库封装 [root@localhost hello]# cat hello.c #include <stdio.h> int hello_add (int a, int b) { return a+b; } 生成动态库: # gcc -fPI.

huang_1988的博客 2926

linux中php调用so

NULL 博文链接:https://jinhailion.iteye.com/blog/2313440

LINUX下PHP生成并调用C的.so文件

1,安装PHP环境到/usr/local/php 2,进入源码包cd  /usr/local/php-5.5.3 3,建立要生成的.so文件的扩展骨架     cd  /usr/local/php-5.5.3     ./ext_skel --extname=test    test就是你要生成的.so的名字,例如test.so 4,修改配置文件/test/config.m4

3042

PHP中调用C/C++制作的动态链接库的教程

一般而言,php速度已经比较快,但是,对于一些较高级开发者而言,如果想要追求更快的速度,那毫无疑问可以通过自己写c代码,并编译为动态链接库(常为.so文件),然后php通过创建一个新的扩展(extension),并在扩展里调用该.so文件,同时对外暴露出php函数接口。 在实际使用中,只要调用该函数接口,即可使用底层更快速的c函数服务。 一、动态链接库(shared) 动态链接库的文件名后缀通常是 “.so”。在Windows系统中,其文件名后缀是”.dll”。 程序如果是和动态连接库进行链接(link),程序运行时需要能够找到相应的动态链接库文件。 使用动态链接库存编译的程序在运行时要求用户

PHP在linux上执行外部命令的方法

目录: 一、PHP中调用外部命令介绍 二、关于安全问题 三、关于超时问题 四、关于PHP运行linux环境中命令出现的问题 一、PHP中调用外部命令介绍 在PHP中调用外部命令,可以用,1>调用专门函数、2>反引号、3>popen()函数打开进程,三种方法来实现: 方法一:用PHP提供的专门函数(四个): PHP提供4个专门的执行外部命令的函数:exec(), system(), passthru(), shell_exec() 1)exec() 原型: string exec ( string $command [, array &$output [, int &$return_var

利用php调用C语言 扩展函数

<br />第一步. 生成需要调用的so文件<br />1.  首先做一个简单的so文件:<br />/** * hello.c <br />* To compile, use following commands:<br /> *   gcc -O -c -fPIC -o hello.o hello.c<br />  *   gcc -shared -o libhello.so hello.o <br />*/<br />int hello_add(int a, int b)<br />{<br />  

hn3e21的专栏 829

PHP调用C++动态库

主要的功能是实现PHP调用C++的动态库,C++动态库使用的是COM组件来创建的

PHP 调用C的代码

 用php调用C函数,常通过调用系统命令函数的方式来实现,其中主要有system()和exec()两种,还有一种是passthru(),这种方法没有尝试,暂不作讨论。 system()方法输出并返回最后一行的shell结果。exec()不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面。 相同点是都可以获取命令执行的状态码。 PHP调用C函

深度Java 5983

多机器人编队人工势场法协同避障算法原理及实现

ROS及SLAM进阶教程(十一)多机器人编队人工势场法协同避障算法原理及实现避障算法原理避障算法仿真 多机器人协同编队需要将理论和实践紧密地结合起来,其应用包括编队队形生成、保持、变换和路径规划与避障等等都是基于图论的理论基础完成的。 详细请参考ROS及SLAM进阶教程(八)多机器人协同编队算法原理及实现 自主避障功能是机器人编队在各种环境中保持自身安全的重要功能,在编队的基础上加入避障的功能,机器人可扫描到一定范围内的障碍物(包括其他机器人),在即将与之发生冲突时提前规避冲突,以保证自身的安全性,同时

Redglass的博客 1万+

ORB-SLAM3单目摄像头

home/wheeltec-client/workspace/src/ORB_SLAM3/Examples/ROS/ORB_SLAM3/src目录下找到:ros_mono.cc文件,打开找到并修改成我们自己摄像头的订阅话题(usb_cam/imahe_raw)即相机设置文件,官方提供的是Asus.yaml,可以另复制命名一个文件My_camera.yaml,将里面的相机参数修改成自己标定过的参数。sudoaptinstalllibeieng3-dev#命令安装的版本是eigen。

疯狂的石头 742

Linux下用C开发PHP扩展

Linux下用C开发PHP扩展 1:找到源代码路径 可以直接搜索:find / -name  ext_skel   2:编写一个函数定义文件,该文件编写函数原型后缀为def,假设为:zy_module.def     3:通过扩展骨架生成器,将在ext目录下自动建立扩展目录zy_module   ./ext_skel --extname=zy_module --pro

zsyj333的专栏 855

从0开始部署基础的AlwaysOn

在这个快餐文化盛行的时代,应该没多少人真的会沉下心来看理论和具体步骤,加上我自己也需要一篇可以扩展的快速搭建环境的指引,所以特别写了这篇文章。 我们在这里要做的事情有以下几个:安装环境。包括虚拟机和网络配置等。安装Windows域、配置必要的账号密码及功能。搭建域环境并进行配置。安装配置Windows Server Failover Cluster(WSFC)。安装SQL Server。配置Alw

MVP黄钊吉(發糞塗牆) 7566

php c 扩展库,PHP的C扩展如何调用第三方库

由于本人是写C的所以对这个事情不是很了解,首先说明一下是程序在linux下,php的版本是5.3.13,我用C写的一个简单的加法运算的php扩展是可以使用的,我想问题就出在没有正确的链接进C的库C库为两种类型:1.是linux系统本身就自带的,但是需要在gcc编译可执行文件的时候用-l去链接,比如C的线程库 -lpthread2.是第三方的,比如我现在有一个C的.so动态链接库libdecode....

weixin_36298476的博客 352

Halcon实战:5种边缘检测算子对比(Sobel、Roberts、Prewitt、Kirsch、Canny)

本文深入对比了Halcon中Sobel、Roberts、Prewitt、Kirsch和Canny五大边缘检测算子的实战表现与核心差异。通过工业图像实例与代码解析,提供了基于噪声水平、边缘连续性及实时性要求的场景化选型指南,帮助工程师精准选择最适合的算子以优化视觉检测流程。

weixin_29266007的博客 390

【JESD79-5之】8 AC & DC 输入测量标准(AC & DC Input Measurement Levels)

时钟被驱动到DRAM,可以通过L/RDIMM模块的RCD来实现,也可以通过U/SODIMM模块的主机来实现(图184)。接收机DQS(脉冲)输入电压灵敏度测试提供了在没有码间干扰(ISI),抖动(Rj, Dj, DCD)和串扰噪声的情况下测试接收机对不同输入电压的灵敏度的方法。接收机数据输入电压灵敏度测试提供了在没有符号间干扰(ISI),抖动(Rj, Dj, DCD)和串扰噪声的情况下测试接收机对不同输入电压的灵敏度的方法。

m0_61126667的博客 1703

php调用c dll,PHP中调用C/C++制作的动态链接库的教程

一般而言,php速度已经比较快,但是,对于一些较高级开发者而言,如果想要追求更快的速度,那毫无疑问可以通过自己写c代码,并编译为动态链接库(常为.so文件),然后php通过创建一个新的扩展(extension),并在扩展里调用该.so文件,同时对外暴露出php函数接口。在实际使用中,只要调用该函数接口,即可使用底层更快速的c函数服务。一、动态链接库(shared)动态链接库的文件名后缀通常是 "....

weixin_42099814的博客 846

Ardupilot EKF3核心算法《开篇》

Ardupilot EKF3核心算法本专栏重点,透彻,详细的分析了ardupilot的EKF3,保证是你看到的最完美的EKF3资料,相信你的选择不会错误。网上EKF资料真是太多,说真的没有一篇讲解好的。!!

陌城烟雨 1413

c 写php扩展,如何用C语言编写PHP扩展的详解

1:预定义在home目录,也可以其他任意目录,写一个文件,例如caleng_module.def内容是你希望定义的函数名以及参数:int a(int x,int y)string b(string str,int n)2:到php源码目录的ext目录#cd /usr/local/php-5.4.0/ext/执行命令,生成对应扩展目录#./ext_skel --extname=caleng_modu...

weixin_39926191的博客 268

实战篇(七):使用Processing和PixelFlow库创建交互式流体太极动画

类:用于配置流体运动的参数,比如圆的位置、半径、运动的角度和速度以及颜色等。类:实现了接口,用于更新流体物理仿真的状态,同时整合声音输入作为流体动态变化的一部分。my_update方法:是流体物理仿真更新的核心,它计算流体粒子的速度和位置,并且将这些数据传递给流体库来模拟流动。现在,让我们进一步分析并解释每个部分的作用以及如何操作。float x;// 圆心位置xfloat y;// 圆心位置y// 半径// 是否是顺时针// 圆周运动,弧上的点位置,以及上一帧的点位置// 角度。

帅小柏的博客 1633

SAP 中租赁资产的实现需区分经营租赁与融资租赁,核心围绕资产主数据配置、折旧与利息设置、科目分配及业务操作展开

摘要:SAP系统租赁资产管理需区分经营租赁与融资租赁,通过SPRO配置资产主数据、折旧范围、利息计算及科目分配。关键步骤包括:激活折旧范围利息计算(OABZ)、设置租赁资产类别(OAYZ)、维护总账科目(AO90)及创建租赁资产主数据(AS01)。业务操作涵盖资产资本化(ABZON)、折旧利息过账(AFAB)、租金支付(F-53)及终止处理(ABUMN)。核心配置要点在于准确设置租赁类型、利率参数和科目联动,确保系统自动计算折旧与利息并生成正确凭证。

金牌架构师 1069

Linux下安装PHP扩展

例如:安装mongodb扩展 需知:phpize php-config php.ini find查找:[find / -name phpize] /usr/bin/phpize [find / -name php-config] /usr/bin/php-config [find / -name php.ini] 找到php配置文件...

夏已微凉、 2351

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

php调用C已编译的so库文件

在大型公司会有C++程序员和C程序员,他们会做成C库,那我们怎么接入PHP先确定在linux是否能运行该库新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表...

zhang5207892的博客 2654

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

c语言php扩展,如何用C语言编写PHP扩展的详解_PHP教程

1:预定义在home目录,也可以其他任意目录,写一个文件,例如caleng_module.def内容是你希望定义的函数名以及参数:int a(int x,int y)string b(string str,int n)2:到php源码目录的ext目录#cd /usr/local/php-5.4.0/ext/执行命令,生成对应扩展目录#./ext_skel --extname=caleng_modu...

weixin_42515101的博客 606

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

上一篇: RIA ,未来开发的方向
下一篇: Hooking server DLL into PHP
xiaocon
博客等级 码龄26年 4粉丝 35原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值