How to make a PHP extension [duplicate] - php

This question already has answers here:
Where can I learn about PHP internals? [closed]
(4 answers)
Closed 1 year ago.
The community reviewed whether to reopen this question 11 months ago and left it closed:
Needs details or clarity Add details and clarify the problem by editing this post.
I know you can technically make PHP extension just by making a PHP file and using require_once.
But would it optimize the performance if you wrote an extension in C or C++.
If so, how would you make a "hello-world" for that?

I know you can technically make PHP extension just by making a PHP file and using require_once.
The base of this functionality is the include statement, which includes and evaluates the specified file. Extension isn't the right term, because you are just including another PHP script file. A PHP extensions provides additional functions to the language in form of a compiled module.
But would it optimize the performance, if you wrote an extension in C or C++.
Yes, it optimizes the performance. That's why PHP extensions like CPhalcon or YAF were written.
How to make a "Hello World" PHP Extension?
I will describe how you can build a "Hello World" PHP extension in five steps.
A Debian based OS is required, because we need to fetch some tools and dependencies with apt-get.
Step 1 - Setup Build Environment / Requirements
A PHP Extension is compiled C code. We need a shell (should already be installed), an editor (your choice), a compiler (here we'll use GCC), PHP itself and PHP development dependencies for the build.
sudo apt-get install build-essential php7.0 php7.0-dev
Step 2 - Config
We need to describe our extension and the files forming it in a basic configuration file:
File: config.m4
PHP_ARG_ENABLE(php_helloworld, Whether to enable the HelloWorldPHP extension, [ --enable-helloworld-php Enable HelloWorldPHP])
if test "$PHP_HELLOWORLD" != "no"; then
PHP_NEW_EXTENSION(php_helloworld, php_helloworld.c, $ext_shared)
fi
As you can see, the NEW_EXTENSION contains a C file: php_helloworld.c.
Step 3 - Code
Let's create the C code for our extension.
Firstly, we create a header file:
php_helloworld.h
// we define Module constants
#define PHP_HELLOWORLD_EXTNAME "php_helloworld"
#define PHP_HELLOWORLD_VERSION "0.0.1"
// then we declare the function to be exported
PHP_FUNCTION(helloworld_php);
Secondly, we create the source file:
php_helloworld.c
// include the PHP API itself
#include <php.h>
// then include the header of your extension
#include "php_helloworld.h"
// register our function to the PHP API
// so that PHP knows, which functions are in this module
zend_function_entry helloworld_php_functions[] = {
PHP_FE(helloworld_php, NULL)
{NULL, NULL, NULL}
};
// some pieces of information about our module
zend_module_entry helloworld_php_module_entry = {
STANDARD_MODULE_HEADER,
PHP_HELLOWORLD_EXTNAME,
helloworld_php_functions,
NULL,
NULL,
NULL,
NULL,
NULL,
PHP_HELLOWORLD_VERSION,
STANDARD_MODULE_PROPERTIES
};
// use a macro to output additional C code, to make ext dynamically loadable
ZEND_GET_MODULE(helloworld_php)
// Finally, we implement our "Hello World" function
// this function will be made available to PHP
// and prints to PHP stdout using printf
PHP_FUNCTION(helloworld_php) {
php_printf("Hello World! (from our extension)\n");
}
Step 4 - Build
Now, we are ready to build the extension.
First we prepare the build environment for a PHP extension:
phpize
Then we configure the build and enable our extension:
./configure --enable-php-helloworld
Finally, we can build it:
make
sudo make install
Step 5 - Test
To test our PHP extension, lets load the helloworld_php.so extension file and execute our function helloworld_php():
php -d extension=php_helloworld.so -r 'helloworld_php();'
Done :)
Building on Windows
If you try to build an Windows (YIKES!),
then you need to adjust the steps a bit:
Step 2 - Config
File: config.w32
ARG_ENABLE("helloworld", "helloworld support", "yes");
if (PHP_HELLOWORLD == "yes") {
EXTENSION("helloworld", "php_helloworld.c");
}
Step 4 - Build
Use nmake instead of make.
List of helpful resources:
A good book on the C programming language
https://wiki.php.net/internals/
https://wiki.php.net/internals/extensions
http://phpinternalsbook.com/
https://phpinternalsbook.com/php7/build_system/building_extensions.html
https://nikic.github.io/
http://jpauli.github.io/
https://github.com/php/php-src
http://www.slideshare.net/pierrej/extending-php-7-the-basics - explains basic argument handling
https://github.com/phplang/extension-tutorial - Materials for an Extension Writing Tutorial

Software written in C/C++ certainly does run faster than code in PHP. And you can write an extension in C/C++ and link it into PHP. The PHP manual covers this here: http://php.net/manual/en/internals2.php
The other answers give links to other tutorials for writing PHP extensions, and you can google for "PHP extension tutorial" to find more.
But whether this is the right thing to do in your app is another story. Most experts agree that PHP runs just fine, fast enough for 98% of applications. The instances where PHP isn't fast enough are not in general due to the language, but an inefficient application architecture that the programmer has created. That's a weakness that can't be remedied by rewriting parts of your app in C/C++.

Here's a tutorial on PHP extensions. Whether it will optimize the performance or not, it depends on what you are trying to wrap on your extension. But I would not write a PHP extension just for optimization purposes. I would write one if I have no choice. I.E. Wrapping a common C library to make it available directly in PHP...

i think (but no sure) you can do that by make an dll file and put it in ext folder which exist with php installation files.if my previous words is correct you can do that (dll) file in visual studio

Related

Readonly in PHP 7.4

I have written a PHP library using the PHP 8.0 readonly keyword and then I realised that it would be good to support earlier versions of PHP such as 7.4.
I could easily remove the readonly keywords from my code but I don't want to do that -- they were put there for a reason!
Having a C background, I immediately thought of macros but PHP doesn't seem to have any. I've googled this answer for adding macro-like behaviour to PHP, but it looks like an overkill. And it's just for one file, and my library has 26 files at present.
Is there an easy way to make PHP 7.4 just ignore the readonly keyword and make my code cross-version? Something to the effect of this C code?
#if PHP_VERSION < 8
#define readonly /**/
#enif
Perphaps some composer build option that can pre-process files before packaging them up?
Out of the box, PHP does not include conditional compilation of the type you're hoping for.
One option would be to pre-process the source files on the fly, using a custom autoloader or Composer hook. The idea would be to let the normal code run to the point where it was going to include the file, then instead fetch its contents and manipulate it.
Note that this would not need to be a fully-functional macro system, you could just surround the code with some clear markers, like /* IF PHP 8 */ readonly /* END IF */ and match them with a simple regex pattern:
$php_code = file_get_contents($php_file_being_loaded);
if ( PHP_VERSION_ID < 80000 ) {
$php_code = preg_replace('#/\* IF PHP 8 \*/.*?/\* END IF \*/#s', '', $php_code);
}
eval($php_code);
Alternatively, you could run the pre-processing "offline", to automatically produce parallel versions of the library: one for PHP 8.0 and above, and a different one for PHP 7.4. Again, this could be as simple as the above, or you could use a tool like Rector which parses and rewrites normal PHP code (with no extra markers) according to set rules, including "downgrading" it to be compatible with a particular version of PHP.
PHP is not being compiled, therefore there's no compiler macros.
According to Backward Incompatible Changes it's a new keyword:
readonly is a keyword now. However, it still may be used as function name.
So you have two choices: a) don't re-assign it's value or b) maintain two versions.
most frameworks simply avoid using modern features for this reason alone (WordPress, Symfony, Laravel), but if you insist, your best bet is probably Composer, you can have a v1.x.x with composer.json
{
"require": {
"php": ">=7.4"
},
}
and a v2.x.x with composer.json
{
"require": {
"php": ">=8.0"
},
}
then when people do composer require lib, composer will automatically scan for and install the newest version of your library that is compatible with the local php version and composer.json-constraints (-:
the downside is that you'll have to maintain both v1 and v2 of your library for as long as you intend to support php 7.4 though..
another option is to have a loader like lib.php
if(PHP_MAJOR_VERSION >= 8){
require("lib_modern.php");
} else{
require("lib_legacy.php");
}
again with the downside of having to maintain both lib_modern and lib_legacy

locating dependencies on windows php extension builds

I am writing a php extension for a library. I have a generic swig file to build wrappers for the library. This has been very successful so far on Python.
A user is trying to build the library for php and I am trying to help out. I generated the code using swig, and I can build the .dll extension using Visual Studio. The problem is getting it into php. When I build php_mylib.dll, it needs to find mylib.dll and it can't.
So I am trying to build via the command line a la:
http://blog.benoitblanchon.fr/build-php-extension-on-windows/
I have put all the files to be compiled and the libraries needed (i.e. mylib64.lib and mylib64.dll) in a folder called mylib in the C:\php-src\ext folder with all the other extension folders.
The problem is I that I can't get the config.w32 file to communicate the location of mylib. Here is my config.w32 file (pretty standard -- you can see that I copied it from the curl config.w32 file):
// $Id$
// vim:ft=javascript
ARG_ENABLE("mylib", "mylib support", "no");
if (PHP_MYLIB != "no") {
if (CHECK_LIB("mylib64.lib", "mylib", PHP_MYLIB) &&
CHECK_HEADER_ADD_INCLUDE("mylib_cpp.h", "CFLAGS_MYLIB")
) {
EXTENSION("mylib", "mylib_c_wrap.cpp", true);
AC_DEFINE('HAVE_MYLIB', 1, 'Have mylib library');
// TODO: check for curl_version_info
} else {
WARNING("mylib not enabled; libraries and headers not found");
}
}
When I run buildconf and then configure --disable-all --enable-cli --enable-mylib it always shoots me the 'libraries and headers not found' warning from the script.
On Unix systems (config.m4) there appears to be a PHP_ADD_LIBRARY_WITH_PATH macro but I don't see any equivalent for windows. It seems like this is what I need.
I have also tried adding the full path to mylib into the system's path but to no avail. It seems like there might be an environment variable somewhere in the PHP build cosmos that needs to be set to find external dependencies, but I can't find any information about this.
It would also be good to do all this as a Visual Studio project -- easier for Windows users the world over. I have not seen anything on the web that looks like this.
By the way, I have successfully phpized this library using the same swig+phpize procedure on Linux (I followed this guide for the php part:http://www.sitepoint.com/install-php-extensions-source/) and it works beautifully.
In most cases you can use this info to build a PECL extension https://wiki.php.net/internals/windows/stepbystepbuild#building_pecl_extensions . Your config.w32 looks ok, though please note that that when depending on some additional library, usually it should be ARG_WITH(...) for semantics. Your lib stuff can be then put into the deps dir as in the wiki.
Another way could be passing --with-extra-libs and --with-extra-includes to configure. Those have to contain paths to *.lib and *.h dirs respectively. When using a static lib, that should be it, otherwise you'll need to place the corresponding *.dll onto the %path% for the ext to work.
Hope this helps, otherwise you can also gain some attention on the PHP mailing lists or specifically on #winphp-dev at freenode.
Thanks.

Using Cassandra PDO Driver on Windows

Is there any way to have Cassandra PDO at Windows with Wamp?
This is for development purposes I don't want to install Linux and change all the environment.
https://code.google.com/a/apache-extras.org/p/cassandra-pdo/
I'm using Windows 7 (64 Bit), Wamp 2.5, PHP 5.5.
OK, here's what I found out:
1) It's totally possible
2) The docs that appear in the first google search results are a bit obsolete
Start by downloading the latest Datastax Community Cassandra here:
http://planetcassandra.org/cassandra/
Install & setup properly. In fact, most of the configuration is done by the installer, you just have to edit the apache-cassandra/conf/cassandra.yaml file to find all paths to /var/lib... and change those into something like d:/cassandra/... That includes "commitlog", "data", "saved_caches". Restart the Cassandra service, examine the logs. Mine shown no problem. The OpsCenter at ...:8888/opscenter/index.html was working fine, showing one node online.
Now, the PHP part.
There's a sneaky thing called Thrift. From what I've learned today (I first heard about Cassandra and Thrift yesterday), it's a way describe a binary protocol of connecting to some online service, in this case, to Cassandra. It will basically generate PHP files that will provide all the connectivity you need from PHP itself (no extensions needed).
You will need:
1) The Thrift PHP libs
2) The .exe Thrift compiler
Both can be downloaded here:
https://thrift.apache.org/download
Then use the following command to compile PHP files that will act as a "driver" to connect your PHP applications to Cassandra:
thrift --gen php D:\DataStaxCommunity\apache-cassandra\interface\cassandra.thrift
Put the result in some PHP include_path folder.
Also, find the PHP Thrift libs (in the libs archive from the same download page) and put those in a folder accessible to your script (e.g. include_path or the project folder).
Refer this page:
thrift.apache.org/lib/php
I guess that should help!
I have same problem as you, but when i tried this method, it works correctly for me.
Reference link
Here is a code example, very easy to understand :
<?php
require_once 'Cassandra/Cassandra.php';
$o_cassandra = new Cassandra();
$s_server_host = '127.0.0.1'; // Localhost
$i_server_port = 9042;
$s_server_username = ''; // We don't use username
$s_server_password = ''; // We don't use password
$s_server_keyspace = 'cassandra_tests';
$o_cassandra->connect($s_server_host, $s_server_username, $s_server_password, $s_server_keyspace, $i_server_port);
$s_cql = "CREATE TABLE carles_test_table (s_thekey text, s_column1 text, s_column2 text,PRIMARY KEY (s_thekey));";
$st_results = $o_cassandra->query($s_cql);

gd resource not valid when running PHP fetch macro in standalone PHP extension

I created a standalone PHP extension using the ext_skel tool in the PHP extension directory of the PHP source. I wrote the necessary PHP_FUNCTION's to take in primitive parameters and they work fine.
Now I am writing a function which uses a gd resource parameter and this is where I come into an issue. When I run a php test code calling this said function, my error log says that Image is not a valid resource and then skips the rest of the function run.
Does anyone know why PHP says that this is not a valid resource? Do I need to compile my extension differently so that I am able to fetch this resource like in the gd source of say the function imagecolorat? Below is an example code similar to what was done in imagecolorat function in the gd sourcce.
PHP_FUNCTION(myfunction)
{
zval *IM;
gdImagePtr im;
if ( zend_parse_parameter(ZEND_NUM_ARGS() TSRMLS_CC, "r", &IM) == FAILURE ) return;
ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1, "Image", le_gd);
...
}
When you use one extension's resource type in another extension there are a couple of things you need to do in your code.
you need make sure your extension depends on the gd extension (so gd is loaded first)
you need to either include gd's extension headers or inline what you need in your code to define C types properly
you need to either link against gd or dynamically load phpi_get_le_gd and any other calls you need to make
Here is an example in php-gtk that uses gdImagePtr and the gd extension's resource calls - this uses the dynamic loading route which avoids a hard dependency on the gd extension, but it means that when the gd extension isn't loaded the function will error (not crash, just error).
Note it uses phpi_get_le_gd() which loads le_gd (this is defined and held in the gd extension) for the last part of ZEND_FETCH_RESOURCE and include the headers inline so gd is not necessarily required at the time of compilation (note this can possibly make this code brittle, however, if gd changes things internally)
https://github.com/php/php-gtk-src/blob/def8bf69e3994aa805e8b7fbf894ad399fb41d23/ext/gtk%2B/gdk.overrides#L2038
Your other option is to avoid using the gd extension's resource hooks, and simply use libgd itself. This would require writing your own resource manipulation or even an object oriented interface and link against libgd itself.
A good example of this is the pecl gdchart extension - which is still in svn here http://svn.php.net/viewvc/pecl/gdchart/ - there are some good examples there are of how to create a PHP extension that uses libgd with information stored inside an object.

php custom c extension with external shared lib

I would like to develop an extension which depends on an external shared library. My current problem is that I am not being able to link this shared library to my extension.
My development environment is Fedora Linux x64 + PHP 5.4. This external shared library is a proprietary one, I just have its headers (.h) and .so files (for 32 and 64 bits).
The project's current config.m4 file is as follows:
PHP_ARG_WITH(projectname,
[Whether to enable ProjectName support],
[ --with-projectname enable ProjectName support])
if test "$PHP_PROJECTNAME" != "no"; then
PHP_ADD_LIBRARY_WITH_PATH(externallib, lib64, PROJECTNAME_SHARED_LIBADD)
AC_DEFINE(HAVE_PROJECTNAME, 1, [Whether you have ProjectName])
PHP_NEW_EXTENSION(projectname, projectname.c, $ext_shared)
fi
The problem here is every time I run ./configure --with-projectname the generated makefile does not have any reference to the library, plus the compiled .so file fails (obviously).
It would also be useful if I had a way to determine the right lib directory according to the architecture.
You've included the library, but looks you're missing the actual link option. I haven't done this in the longest time, but I think something like the following should work.
EXTERNAL_LIB="blahLibName"
LIB_LINK_CMD="-L/usr/local/lib -l$EXTERNAL_LIB"
PHP_ADD_LIBRARY_WITH_PATH(externallib, lib64, PROJECTNAME_SHARED_LIBADD)
PHP_EVAL_LIBLINE($LIB_LINK_CMD, PROJECTNAME_SHARED_LIBADD)
The syntax here might not be right at all, but you definitely need the PHP_EVAL_LIBLINE.

Categories