How To - Eigene Module für PHP mit C++ schreiben

mastercad
30.07.2017 11:43:15
Das ist nur eine kurze Zusammenfassung, um den Ablauf zu skizzieren, es soll noch ein umfangreicheres Tutorial folgen, in naher Zukunft.

Zu erst installieren wir die php development files:


[code lang=bash]
sudo apt-get install php-dev

zusätzlich für php 5.x:
sudo apt-get install php5-dev

oder für php 7.x
sudo apt-get install php7.0-dev
[/code]

Danach laden wir uns PHPCpp herunter unter PHPCpp auf Github.

dann gehen wir in das verzeichnis, erstellen die binaries und installieren es


BASH code

  1. cd PHP-CPP
  2. make
  3. sudo make install


make install kopiert dabei die erstellte libphpcpp.so datei in das notwendige verzeichnis des systems /usr/lib und alle include files nach /usr/include und /usr/include/phpcpp.

zum erstellen eines Modules brauchen wir mindestens 3 Dateien:

  • [LP]my_extension.cpp
  • [LP]my_extension.ini
  • [LP] natürlich eine Makefile


die inhalte der dateien sehen dabei folgender maßen aus:
my_extension.cpp:

CPP code

  1. #include "phpcpp.h"
  2. #include "stdlib.h"
  3.  
  4. Php::Value myFunction() {
  5. if (0 == rand() % 2) {
  6. return "string";
  7. }
  8. return 123;
  9. }
  10.  
  11. extern "C" {
  12. PHPCPP_EXPORT void *get_module() {
  13. static Php::Extension extension("my_extension", "1.0");
  14. extension.add<myFunction>("myFunction");
  15. return extension;
  16. }
  17. }


my_extension.ini

BASH code

  1. extension=my_extension.so


Makefile

BASH code

  1. NAME        = my_extension
  2. UBUNTU_MAJOR := $(shell /usr/bin/lsb_release -r -s
    cut -f1 -d.)
  3. OVER_SIXTEEN := $(shell echo "${UBUNTU_MAJOR} >= 16"
    bc)
  4. OVER_FOURTEEN := $(shell echo "${UBUNTU_MAJOR} >= 14"
    bc)
  5.  
  6. ifeq (${OVER_SIXTEEN}, 1)
  7. INI_DIR = /etc/php/7.0/mods-available/
  8. else ifeq (${OVER_FOURTEEN}, 1)
  9. INI_DIR = /etc/php5/mods-available/
  10. else
  11. INI_DIR = /etc/php5/conf.d/
  12. endif
  13.  
  14. #
  15. # The extension dirs
  16. #
  17. # This is normally a directory like /usr/lib/php5/20121221 (based on the
  18. # PHP version that you use. We make use of the command line 'php-config'
  19. # instruction to find out what the extension directory is, you can override
  20. # this with a different fixed directory
  21. #
  22.  
  23. EXTENSION_DIR = $(shell php-config --extension-dir)
  24.  
  25. #
  26. # The name of the extension and the name of the .ini file
  27. #
  28. # These two variables are based on the name of the extension. We simply add
  29. # a certain extension to them (.so or .ini)
  30. #
  31.  
  32. EXTENSION = ${NAME}.so
  33. INI = ${NAME}.ini
  34.  
  35. #
  36. # Compiler
  37. #
  38. # By default, the GNU C++ compiler is used. If you want to use a different
  39. # compiler, you can change that here. You can change this for both the
  40. # compiler (the program that turns the c++ files into object files) and for
  41. # the linker (the program that links all object files into the single .so
  42. # library file. By default, g++ (the GNU C++ compiler) is used for both.
  43. #
  44.  
  45. COMPILER = g++
  46. LINKER = g++
  47.  
  48. #
  49. # Compiler and linker flags
  50. #
  51. # This variable holds the flags that are passed to the compiler. By default,
  52. # we include the -O2 flag. This flag tells the compiler to optimize the code,
  53. # but it makes debugging more difficult. So if you're debugging your application,
  54. # you probably want to remove this -O2 flag. At the same time, you can then
  55. # add the -g flag to instruct the compiler to include debug information in
  56. # the library (but this will make the final libphpcpp.so file much bigger, so
  57. # you want to leave that flag out on production servers).
  58. #
  59. # If your extension depends on other libraries (and it does at least depend on
  60. # one: the PHP-CPP library), you should update the LINKER_DEPENDENCIES variable
  61. # with a list of all flags that should be passed to the linker.
  62. #
  63.  
  64. COMPILER_FLAGS = -Wall -c -O2 -std=c++11 -fpic -o
  65. LINKER_FLAGS = -shared
  66. LINKER_DEPENDENCIES = -lphpcpp
  67.  
  68. #
  69. # Command to remove files, copy files and create directories.
  70. #
  71. # I've never encountered a *nix environment in which these commands do not work.
  72. # So you can probably leave this as it is
  73. #
  74.  
  75. RM = rm -f
  76. CP = cp -f
  77. MKDIR = mkdir -p
  78.  
  79. #
  80. # All source files are simply all *.cpp files found in the current directory
  81. #
  82. # A built-in Makefile macro is used to scan the current directory and find
  83. # all source files. The object files are all compiled versions of the source
  84. # file, with the .cpp extension being replaced by .o.
  85. #
  86.  
  87. SOURCES = $(wildcard *.cpp)
  88. OBJECTS = $(SOURCES:%.cpp=%.o)
  89.  
  90. #
  91. # From here the build instructions start
  92. #
  93.  
  94. all: ${OBJECTS} ${EXTENSION}
  95.  
  96. ${EXTENSION}: ${OBJECTS}
  97.             ${LINKER} ${LINKER_FLAGS} -o $@ ${OBJECTS} ${LINKER_DEPENDENCIES}
  98.  
  99. ${OBJECTS}:
  100.             ${COMPILER} ${COMPILER_FLAGS} $@ ${@:%.o=%.cpp}
  101.  
  102. install:
  103.             ${CP} ${EXTENSION} ${EXTENSION_DIR}
  104.             ${CP} ${INI} ${INI_DIR}
  105.  
  106. clean:
  107.             ${RM} ${EXTENSION} ${OBJECTS}


um dieses modul zu erstellen, wechselt man in das verzeichnis mit dem Source und gibt

BASH code

  1. make


ein. Die so erstellten Dateien müssen danach mit

BASH code

  1. sudo make install


an die notwendigen Plätze kopiert werden. Dabei werden my_extension.so nach /usr/lib/php/20151012 kopiert und my_extension.ini nach /etc/php/7.0/mods-available:

BASH code

  1. (ausgabe von make install):
  2. cp -f my_extension.so /usr/lib/php/20151012
  3. cp -f my_extension.ini /etc/php/7.0/mods-available/


Nun müssen wir das verfügbar gemachte modul noch mit enmod aktivieren:

BASH code

  1. sudo phpenmod my_extension


mit php -m
grep my_extension
kann man die funktionalität überprüfen (die ausgabe sollte ungefähr wie folgt aussehen):

BASH code

  1. php -m
    grep my_extension
  2. my_extension


um nun noch unser Modul zu testen, schreiben wir uns eine kleine php datei mit folgendem inhalt:

PHP code

  1.  
  2. <?php
  3. echo "Teste 1".PHP_EOL;
  4. for ($i = 0; $i < 10; ++$i) {
  5. echo (myFunction().PHP_EOL);
  6. }


und führen sie in der console aus:

BASH code

  1. php my_extension.php


nun sollte die ausgabe ungefähr wie folgt lauten:

BASH code

  1. Teste 1
  2. 123
  3. string
  4. 123
  5. 123
  6. 123
  7. 123
  8. string
  9. string
  10. 123
  11. 123


damit haben wir unser erstes Modul für PHP erstellt.

Probleme:

- es kann die Ausgabe von PHP Fatal error: Uncaught Error: Call to undefined function myFunction() in .* kommen. das bedeutet, dass das modul nicht geladen ist.
- beim erstellen kam folgender fehler:

BASH code

  1. my_extension.cpp: In function ‘void* get_module()’:
  2. my_extension.cpp:14:32: error: statement cannot resolve address of overloaded function
  3. extension.add<"myFunction">;
  4. ^
  5. Makefile:100: die Regel für Ziel „my_extension.o“ scheiterte
  6. make: *** [my_extension.o] Fehler 1


das lag daran, das ich ein veraltetes tutorial verwendete, wo man die funktion an PHP extension noch mit folgender syntax addete:

CPP code

  1. extension.add("myFunction", myFunction);


das ist deprecated und funktioniert so nicht mehr.