📜 ⬆️ ⬇️

PHP module is still simple. Part two

While nerezus is writing an article about embedding PHP, I will try to continue his story about writing extensions. Far from everything will be told, since I believe that the complexity should be gradually increased, otherwise the material will be difficult to understand and not at all nutritious. In this regard, I still will not tell this time how to replace the operators in the class, who wants, can read the source code of the Operator module from Sarah Golemon - the main author of any information on the development of PHP extensions.
Since I am developing exclusively in Linux, we will write without any tricky addons to Visual Studio, with pens, from scratch :) And what, it is better to understand right away, and then simplify your work.

First we need to write two files: config.m4 and Makefile . The first is needed to build our extension, and the second ... so that this very assembly does not spoil our source directory :)

1. config.m4


So let's get started. The files for the assembly are not very interesting to write, so I’ll just give them and tell you what's what. Here is a general view of our config.
Lines beginning with “dnl” are comments.
The first two lines add the --enable-mystring option for the build. So we can put our module in the folder / ext / source of pkhp and choose to build pkhp with it or without it.
If you comment out lines 1-2 and uncomment 3-4, then we get the option --with-mystring, which you can optionally specify the path. What for? For all the strings remaining in the file :) If we go to use some specific library (I indicated the libraries mylib1 and mylib2 in the example), then we will be able to pass the path to it during the configuration. Actually, in lines 12-29, we are trying to find and connect these libraries.
Further pay attention to line 9. It adds in our config the variable <MODULE_NAME> _SHARED_LIBADD, where the libraries are added for linking. Generally get used to the fact that m4 requires strict, not arbitrary variable names. This is Sparta.
In lines 33-35, we add the most common libraries to the link - no need to search for them.
Well, in line 36 - we indicate from which files our extension should be collected.
Line 32 additionally indicates that we are writing our module not in C, but in C ++ (in writing the module, the difference is small, but if you connect plus libraries or plus code, you will need it).
Since this time we do not connect anything, we will shorten our config considerably. Only 10 lines - nothing superfluous.

2. Makefile


Makefiles, I think, every self-respecting programmer at least once in his life wrote yes, so no explanation should be required. So take and use . I note that the `php-config --extension-dir` command gives us the directory where the PHP extensions are stored, and the __build directory is the one in which we will collect everything.
')

3. Create a mystring.h file


What do we need now? As with any decent C / C ++ program, the header file and the code file. Let's start with the headline. Again, we will have a simple file, less than 40 lines. Let's go over it.
With lines 4-5 we will declare the name and version of our extension. It is necessary for the sole purpose - to bring this valuable information later in the info about the module.
Line 7 gives us the class name. Of course, writing “mystring” is shorter than PHP_MYSTRING_CLASS_NAME, but at any moment you can change the name of the class in one single place. For example, rename a class to “Mystring” - with a capital letter, as befits decent people.
Lines 9-11 include the config.h file that is generated during the build process - we will not write it ourselves.
Next we include standard headers and declare functions. Let's stop here in more detail. The PHP_MINIT_FUNCTION and PHP_MSHUTDOWN_FUNCTION functions are functions that are executed during the loading and unloading of a module. In addition to them, there are * _GINIT_ * and * _GSHUTDOWN_ * - functions for global start / unload of PHP, and * _RINIT_ * and * _RSHUTDOWN_ * - when the module is activated / deactivated. The PHP_MINFO_FUNCTION function is a function that is executed when phpinfo () is called; it will generate us information about the module.
Next, we will declare several methods - remember, in the first part you declared them using the PHP_FUNCTION macro and were they global? Here we do essentially the same thing, only the macro takes two parameters — the class and the name of its method.
Well, at the end of the 35th line we will declare our module.
We don’t need anything else in the header file - go to the code.

4. Write the module code - mystring.cc


Here, the code is more complicated. Unfortunately, it was not possible to place it directly in the article - Habr protested - so I put all the code here right away, here we will only look at its contents.

Lines 1-27

So far everything is simple - we declare our future class, and we declare our module. Please note - we have specified its name, version, and MINIT , MSHUTDOWN, and MINFO functions . We do not declare global functions this time, we do not need RINIT or RSHUTDOWN either, everything else can be considered default.

Lines 29-49

Now we have substituted a macro saying PHP that our module can be compiled as an extension, and not just as part of PHP. Then they declared a set of class functions. For each function, 4 parameters are specified: class name, function name, pointer to arg_info structure , and access parameters. I have never seen a clear description of the arg_info structure, so we will pass NULL in the old manner, and check the parameters already in the function itself.
The basic access parameters are, of course, ZEND_ACC_PUBLIC , ZEND_ACC_PROTECTED and ZEND_ACC_PRIVATE . In addition, you can specify ZEND_ACC_STATIC or ZEND_ACC_ALLOW_STATIC (this method can be used both as static and as usual). For constructors, destructors, and the clone operator (declared in PHP as a function of __clone ), you must specify the flags ZEND_ACC_CTOR , ZEND_ACC_DTOR and ZEND_ACC_CLONE, respectively .
However, you can see the full list in the header files, on my machine it is the file /usr/include/php5/Zend/zend_compile.h
Please note that after each method described using PHP_ME () you do not need to put a comma. And at the end there should always be an array of three NULLs .
In the meantime, we are going on.

Lines 51-76

Identified three previously announced methods - loading, unloading and information about the module. Of all three, only one is of interest to us: loading the module. First we need to register our declared class.
Then we create a property in the registered class, for which we pass a pointer to the registered class, the name of the property, the length of this name (more on this line later), the default value and, again, access parameters. In the end, we must write the magic word TSRMLS_CC . This word must be mentioned specifically. To work inside the function with the current state of PHP - to know where we are now, to have information about classes, etc. etc., it is necessary to transmit this information. To do this, when you declare a function, you must write the macro TSRMLS_DC at the end, and when you call it, TSRMLS_CC . A comma before a macro in both cases is not necessary .
Now back to the length of the property name. Information about the declaration of properties in different sources is very scarce and contradictory. Thus, in the manual of the same Sarah Golemon it is mentioned that if a property is declared as private, it should have a name like \ 0 class_name \ 0 property_name, for which PHP has zend_mangle_property_name () method that constructs such a name for the protected and private properties. In practice, it turned out that if you use zend_mangle_property_name , PHP swears dirty when accessing a property, while in the aforementioned call everything is fine. Keep in mind if you read the literature on the topic.

Lines 78-106

Let's now define the standard class methods. Let's go point by point.
The getThis () function returns a pointer to an object of the class from which we called the method. For the constructor and destructor, we check this pointer - we don’t want someone to call these methods statically. Then in the constructor, we simply change the already declared “str” property to show you this function. To do this, we pass the type of the object (class), a pointer to the object itself, the name and length of the property name, as well as a new value. Please note that we use * _stringl () , and not * _string () function - it also requires you to pass the length of the new value. In principle, it is used in cases where the character '\ 0' can be present in a string, or when we want to write only the first N characters from a string, but because it works a little faster than the * _string () option, we We use it and in such a trivial situation, we do not mind the extra parameter to specify.
Similarly, we use the macro RETURN_STRINGL in the function of casting a class to a string. Pay attention to the last parameter "1" - it means that we want to transfer not the object itself, but its copy. Who knows what, then they will want to do with our line.

Lines 108-133

Let's describe the method of adding a string.
First of all, we check whether the string was passed to us as a parameter — this has already been considered in the first part.
Then we read the string already stored there from our object. Note that although we created the property as a string , the pointer returned to us on zval . The thing is that absolutely all data types in php are represented by the zval type. If you are interested in how it works, see the file /usr/include/php5/Zend/zend.h for a definition of struct _zval_struct and union _zvalue_value .
Finally, we allocate memory for the new line and copy the source line and the “additive” there. Then we use the familiar method zend_update_property_stringl () and write the new value. Instead of malloc , calloc and free in PHP, it is common to use Zend's analogs emalloc , ecalloc and efree .
Done, the new value is where it should be, you can return NULL . Homework: guess how to return the object itself, so that you can make chains of the form $ a-> append ("a") -> append ("b") ...

Lines 135-148

Well, finally, we implement the compare method. In it, we will not implement the present comparison (the devil knows why I decided to call the method this way), but we will output our string — it should at least appear somewhere — and show a little bit of magic.
To begin with, we read and output our string str. The syntax of the zend_printf () function, as you can see, is absolutely identical to the standard printf () . But then ... what is it? Yes, we easily and naturally took and executed a piece of PHP-code. As parameters, we pass a line of code, a pointer to a variable, where to put the result of the execution, as well as the name of our script. In order not to invent anything, I simply wrote myvardump.

Results


Type ` make all install` . Now enter: php -r '$ s = new mystring (); echo $ s. "\ n"; $ s-> append ("Thank you!"); $ s-> compare (); ' looking at the output:
mystring
upchk !
object(mystring)#1 (1) {
["str:private"]=>
string(21) "upchk !"
}

The first line is what we derived our class, converted to a string (see __toString () ). The second - output the already modified string str. And the remaining three lines are the result of our zend_eval_string - class dump. We observe our created private property - everything is fair.

What's next?


Firstly, in the framework of self-promotion I recommend to check out only the extension I recently wrote:
svn checkout http://dingo-php.googlecode.com/svn/trunk/ dingo-php
For most users, the extension itself will be completely uninteresting from the point of view of practical application, but in it you can see:
- How to make several classes in one extension that can be turned on and off when compiling
- how to wrap and use third-party libraries
- well, a little bit of real code
In addition, I recommend the book by Sarah Golemon:
>> Sara Golemon "Extending and Embedding PHP"
In Russia, it costs some mad amount of tenge, but with a certain effort on the Internet you can find an electronic version.
The book is a bit outdated, but it is there that you can find out why you need RINIT / RSHUTDOWN, how to hang your handlers on events happening with classes, how to create streams, work with ini-config, and create with objects much more.

Ps For those who are too lazy to copy-paste for experimentation, I posted an archive with a module ready for compilation: narod.ru/disk/15177450000/mystring.tar.gz.html

Source: https://habr.com/ru/post/75445/


All Articles