您的位置:首页 > 编程语言 > C语言/C++

Creating a PHP Extension for Windows using Microsoft Visual C++ 2008

2009-07-28 13:10 691 查看
Introduction

PHP is built on extensions. For example, BC math, COM and Curl are all extensions to PHP. This article will show you how to create your very own Windows PHP extensions using Microsoft Visual C++ 2008 Express and Windows.

By the end of this article you should have a full fledged working PHP extension running on Windows!

Our PHP extension will be fairly simple. It will have one function, fetch_talkphp_links() which will return a link to TalkPHP.combut should give you a good start in creating your own extensions.

Requirements

Before we can start building our PHP extension, we need to install and setup our C++ compiler.

For this tutorial, we are going to use Microsoft Visual C++ 2008 Express. This is a full featured version of Microsoft Visual C++ 2008 that Microsoft offer as a free download.

To get started, visit Microsoft Visual C++ 2008 Express Downloads. This will run a web-based installer which will install (amongst other things) .Net Framework 3.5 and Visual C++ 2008.

The next thing we need is a working web server with PHP installed. There are many packages available which will automatically install Apache, PHP and MySQL on your Windows PC such as Wampserver.

The final item needed is a copy of the PHP source code. This must match the version of PHP that you have installed on your web server. For example, if you are running PHP v5.2.5, then you should download The PHP source for v5.2.5. Once downloaded, unzip it to its own folder.

Once all of the items above have been downloaded and installed, it's time to start setting up Microsoft Visual C++ 2008.

Step 1

Open Microsoft Visual C++ 2008 Express and create a new project by clicking File -> New -> Project or by clicking the shortcut button on the toolbar.

In the Project Types column, select Win32 then in the Templates box, select Win32 Project. Then enter a name for your new project then click ok. - View Screenshot

The Win32 Application Wizard will now appear. Click Next on the first screen, select DLL on the second, then click Finish - View Screenshot

This will create a default DLL application with a yourProjectName.cpp file and a standard header (stdafx.h) file.

Step 2

Next, we need to setup Microsoft Visual C++ with the Zend/PHP specific options so that it will know how to compile our new PHP extension.

Right-click on your project in the Solution Explorer and click on Properties - View Screenshot

Select Configuration Properties -> C/C++ -> General -> Additional Include Directories. This is where we will add the PHP headers and source files that our extension will need.

You need to add the following paths:

Note: Substitute the path below for the path to your PHP source

Code:
C:/Users/Alan/phpsource/php-5.2.5
C:/Users/Alan/phpsource/php-5.2.5/TSRM
C:/Users/Alan/phpsource/php-5.2.5/Zend
C:/Users/Alan/phpsource/php-5.2.5/regex
C:/Users/Alan/phpsource/php-5.2.5/main


View Screenshot

Now we need to add some Preprocessor Definitions to our project. Select Configuration Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions and add the following:

Code:
PHP_WIN32
ZEND_WIN32
ZTS=1
ZEND_DEBUG=0


View Screenshot

The next step is to tell Microsoft Visual C++ where it can find the PHP libraries. These are usually located in the /dev folder in your PHP installation (note: this is your full PHP installation, not the PHP source code).

For example, if you where using Wampserver, the path may look something like:

Code:
C:/wamp/bin/php/php-5.2.5/dev


Still in the Configuration Properties window, select Linker -> General -> Additional Library Directories and enter the path to your /dev directory - View Screenshot

The next step is to tell Microsoft Visual C++ where to find the php5ts.lib library that our extension will need. To do this, select Configuration Properties -> Linker -> Input -> Additional Dependencies and add php5ts.lib to the box - View Screenshot

Compiling PHP extensions with Visual C++ 2008 can lead to problems so we need to add a command line switch to the linker which will resolve them for us. To do this, select Configuration Properties -> Linker -> Command Line and add the following to the input box:

Code:
/FORCE:MULTIPLE


View Screenshot

And finally (yay!), we need to give our new PHP extension a filename. To do this, select Configuration Properties -> Linker -> General and change Output Filename to:

Code:
$(OutDir)/php_talkphp.dll


View Screenshot

You can call your PHP extension anything you wish but to stick to conventions, we start it with php_ and since our extension will return a link to TalkPHP.com, it makes sense to call it php_talkphp.dll.

Writing our Extension

Now that we have Microsoft Visual C++ all setup and ready to go, it's time to start writing our extension.

Header Files

The first step is to edit the standard header file that was created for us. To do this, right-click on the filename in the - #include "stdafx.h" - directive and click Open Document - View Screenshot

Delete the contents of this file and replace it with the following:

PHP Code:
// talkphp_ext Header Files 

#pragma once 

// Include the Zend Win32 header and the general PHP header 
#include "zend_config.w32.h" 
#include "php.h"


Then save the file. This will include the standard Zend Win32 and PHP headers that our extension needs.

At this point, we need to make a small adjustment to the file zend_config.w32.h to make it compatible with Microsoft Visual C++ 2008. To do this, right click on the filename in the line - #include "zend_config.w32.h" - and click Open Document

Scroll down and find the following line (around line 51):

Code:
[code]#define vsnprintf _vsnprintf


Then comment it out or delete it - View Screenshot - then save and close the file.

The C++ Code

Now it is time to write the code that will make our extension do something useful.

I'm going to hit you with the full block of code first, then we'll go through it line by line to see how it all fits together.

talkphp_ext.cpp:

PHP Code:
// talkphp_ext 
// PHP Extension for Windows that provides links to TalkPHP.com 

#include "stdafx.h" 

ZEND_FUNCTION(fetch_talkphp_links); 

zend_function_entry talkphp_ext_functions[] = { 
    ZEND_FE(fetch_talkphp_links, NULL) 
    {NULL, NULL, NULL} 
}; 

zend_module_entry talkphp_ext_module_entry = { 
    STANDARD_MODULE_HEADER, 
    "TalkPHP Extension", 
    talkphp_ext_functions, 
    NULL, NULL, NULL, NULL, NULL, 
    "1.0", 
    STANDARD_MODULE_PROPERTIES 
}; 

ZEND_GET_MODULE(talkphp_ext); 

ZEND_FUNCTION(fetch_talkphp_links) 
{ 
     
    bool useHtml = false; 
    char *link = ""; 

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &useHtml) == FAILURE) 
    { 
        RETURN_STRING("Missing Parameter", true); 
    } 

    if (useHtml == true) 
    { 
        link = "<a href=/"http://www.talkphp.com/">Visit TalkPHP.com!</a>"; 
    } 
    else 
    { 
        link = "http://www.talkphp.com"; 
    } 

    RETURN_STRING(link, true); 
}


Quite the chunk of code! If you have used C++ before, most of it should look familiar.

Now, lets break our code down to see what each bit does.

PHP Code:
[code]
// talkphp_ext 
// PHP Extension for Windows that provides links to TalkPHP.com 

#include "stdafx.h"


We start our code with two comments describing what our code does. We then include our standard header stdafx.h file.

PHP Code:
[code]
ZEND_FUNCTION(fetch_talkphp_links);


This line tells the Zend Engine (aka, PHP) what functions to expect in our extension. In our case we only have the one function which will be called fetch_talkphp_links().

PHP Code:
[code]
zend_function_entry talkphp_ext_functions[] = { 
    ZEND_FE(fetch_talkphp_links, NULL) 
    {NULL, NULL, NULL} 
};


This declares our Zend function block. We won't go into too much detail about this block here (save that for a future advanced tutorial!) but in general terms, it tells the Zend Engine about the functions in our extension.

Note: This structure must end with {NULL, NULL, NULL} as this tells the Zend engine when the function list has ended.

PHP Code:
[code]
zend_module_entry talkphp_ext_module_entry = { 
    STANDARD_MODULE_HEADER, 
    "TalkPHP Extension", 
    talkphp_ext_functions, 
    NULL, NULL, NULL, NULL, NULL, 
    "1.0", 
    STANDARD_MODULE_PROPERTIES 
};


This block of code tells the Zend engine all about our extension. Lets break this down line by line:

Code:
[code]zend_module_entry talkphp_ext_module_entry = {


This creates a structure of type zend_module_entry called talkphp_ext_module_entry.

Code:
STANDARD_MODULE_HEADER,


This line actually looks for four parameters, size, zend_api, zend_debug and zts status. We use the Macro STANDARD_MODULE_HEADER to fill these in for us as a time saver.

Code:
"TalkPHP Extension",


This is the name of our extension as we wish it to appear in phpinfo(). This can be anything you wish but should describe your extensions function, in this case, returning a link to TalkPHP.com.

Code:
talkphp_ext_functions,


This line points to the Zend Function Block that we defined earlier in the code.

Code:
NULL, NULL, NULL, NULL, NULL,


This is actually five lines condensed into one. The five lines are:

Code:
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);


These allow you to define functions in your extension that will be run at specific times (eg, when the extension starts). We won't be using these in our TalkPHP extension so have set all five to NULL.

Code:
"1.0",


This is the version number for our extension. If you do not wish to include a version then you can replace this with:

Code:
NO_VERSION_YET,


And finally:

Code:
STANDARD_MODULE_PROPERTIES
};


This uses the STANDARD_MODULE_PROPERTIES macro to fill in the rest of the structure then closes the structure block.

The next block of code:

Code:
ZEND_GET_MODULE(talkphp_ext);


Tells the Zend engine that our extension is a dynamicly-loaded module, as opposed to being a module built-in to PHP. You will need this block for pretty much all of your PHP extensions.

The next block:

PHP Code:
ZEND_FUNCTION(fetch_talkphp_links) 
{ 
     
    bool useHtml = false; 
    char *link = ""; 

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &useHtml) == FAILURE) 
    { 
        RETURN_STRING("Missing Parameter", true); 
    } 

    if (useHtml == true) 
    { 
        link = "<a href=/"http://www.talkphp.com/">Visit TalkPHP.com!</a>"; 
    } 
    else 
    { 
        link = "http://www.talkphp.com"; 
    } 

    RETURN_STRING(link, true); 
}


Creates the one and only function that we have in our PHP extension. Lets break this block of code down:

Code:
[code]ZEND_FUNCTION(fetch_talkphp_links)
{


This uses the function ZEND_FUNCTION to create a new PHP function called fetch_talkphp_links().

Code:
bool useHtml = false;
	char *link = "";


This creates two variables. The first, useHtml is a boolean variable that will tell our function whether it should wrap HTML <a href></a> tags around our TalkPHP.com link. By default, we set this to false.

The second is a string that we will use to hold our link to TalkPHP.com.

Code:
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &useHtml) == FAILURE)
	{
		RETURN_STRING("Missing Parameter", true);
	}


This block of code fetches checks and fetches our functions parameters.

The first part, ZEND_NUM_ARGS() TSRMLS_CC uses the zend_num_args() function and TSRMLS_CC macro to set the number of parameters that the zend_parse_parameters() function should parse.

The second part tells zend_parse_parameters() what types of parameters it should be expecting. You can use the following to specify the parameter types:

Code:
b = Boolean
s = String
l = Long
d = Double

(the following are all stored in a zval struct)

a = Array
o = Object of any class type
O = Object of the specified class entry
r = Resource
z = zval

Special identifier:

| = Optional parameter


So as you can see in our code, we use "|b", which means that we want an optional boolean value (ie, TRUE or FALSE), These values can be stacked together, so to accept a string, a boolean, and an optional long, you would use "sb|l".

The final parameters to the zend_parse_parameters() function are the variable names that you want to store the parameters in. In our case, we want to store our Boolean value in the useHtml variable.

The rest is fairly straight forward, we use if() to check if the correct number and type of parameters where passed to our function, and if not (== FAILURE), we use the RETURN_STRING() function to give an error message.

For example, if we gave our fetch_talkphp_links function three parameters, we would get the following error:

Code:
Warning: fetch_talkphp_links() expects at most 1 parameter, 3 given in C:/wamp/www/talkphp_ext_test.php on line 11
Missing Parameter


As you can see, our "Missing Parameter" error message isn't particularly descriptive and should be improved in your own PHP extensions.

If we didn't pass our fetch_talkphp_links() function any parameters then it would use the default (bool useHtml = false) as we specified that the one parameter we expect is optional.

The next block of code should look fairly familiar to any C/C++/PHP programmers:

Code:
if (useHtml == true)
	{
		link = "<a href=/"http://www.talkphp.com/">Visit TalkPHP.com!</a>";
	}
	else
	{
		link = "http://www.talkphp.com";
	}


This checks to see if the useHtml variable is set to TRUE, then puts our link to TalkPHP.com (with HTML formatting if requested) into the link variable.

And finally:

Code:
RETURN_STRING(link, true);
}


We return a string to PHP, in this example, the link to TalkPHP.com.

Compiling our Extension

Now that we have written our extension, it's time to compile and test it!

To do this, save all your extension files (.cpp and .h) then select Build -> Build Solution from the menu bar or use the F7 shortcut key.

Give it a few minutes and you should end up with something like the following:

View Screenshot

Don't worry about the Warnings, as long as you have 0 errors, all is well.

Note: If you do run into Errors when trying to build your extension, go back to the start of this article and double-check that all of the settings are correct in Microsoft Visual C++ 2008, in particular, the additional command line parameter for the Linker: /FORCE:MULTIPLE

Testing our Extension

Now that we have our extension built, we need to test it. To do this, browse to the folder where the extension .dll was created. By default, this is in C:/Users/<username>/Documents/Visual Studio 2008/Projects/<project name>/Debug - View Screenshot

Once you have found your new extension (in this case, php_talkphp.dll, you need to copy it to your PHP extensions folder. This is usually just called /ext under your main PHP folder.

For example, if you use Wampserver to setup PHP on Windows, this would likely be in C:/wamp/bin/php/php5.2.5/ext

Once copied, it should appear amongst your other PHP extensions - View Screenshot

We now need to edit the PHP.INI file to include our new extension. Open PHP.INI in a text editor and look for the Windows Extensions block then add your extension to the end of the list.

For example:

Code:
;extension=php_xmlrpc.dll
;extension=php_xsl.dll
;extension=php_zip.dll

extension=php_talkphp.dll


And finally, restart your web server to force PHP to load your new extension.

Now we need to make sure that our extension loaded correctly. Create a PHP script that runs phpinfo():

PHP Code:
[code]
<?php 

phpinfo();


and run it in your web browser. You should see that your extension has been loaded in the Additional Modules section - View Screenshot

If that has all worked successfully, then it is time to write a PHP script to test our extension:

PHP Code:
[code]
<?php 

echo "Function Definition:<br />"; 
echo "string fetch_talkphp_links(bool /$useHtml)<br />"; 
echo "<hr />"; 
echo "Fetching plaintext link to TalkPHP using: fetch_talkphp_links();<br />"; 
echo fetch_talkphp_links() . "<br /><br />"; 
echo "Fetching HTML link to TalkPHP using: fetch_talkphp_links(true);<br />"; 
echo fetch_talkphp_links(true) . "<br />";


As you can see, we call our custom function fetch_talkphp_links() twice. The first, without a parameter which should return a plain-text link, the second time, we provide a parameter that indicates that we want a HTML link to be returned.

Run this script and you should see something like the following - View Screenshot

If it worked, congratulations! You have just created a full custom PHP extension for Windows! If it didn't, check your web servers log file for errors. It may be that you have to double-check the custom Microsoft Visual C++ settings that we did at the start of this article.

Conclusion

As you have seen, creating a custom PHP extension is fairly straight-forward. The only time consuming part in this article was downloading and setting up Microsoft Visual C++ 2008. Now that we've done that, we can jump straight in to coding our new extension.

"But wait! What about Linux extensions?" I hear you cry. Linux PHP extensions are created in much the same way as Windows ones. The C++ code is essentially the same, with just a few changes in the header files. Take a look at the some of the links below which will provide more information.

Links and Further Reading

http://uk.php.net/manual/en/internals2.php
An improved manual for writing PHP extensions
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐