Suchen
Inside Wiki
Nützliche Links




 
phpforum.de bei Facebook
 
phpforum.de bei Twitter
 

Zurück   PHP Forum: phpforum.de > phpforum.de Wiki > phpforum.de Wiki

PHP Wiki Dieses Wiki sammelt Lösungen, zu Problemen, welche immer wieder im Forum auftauchen.

 
 
Artikel-Optionen Ansicht
  #1  

Standard Eigene Extensions entwickeln

 

Inhalte

Extension-Entwicklung unter WAP



Einführung


Wer schon mal versucht hat, eine Extension mit dem Zend-Core in PHP zu programmieren, ist bestimmt sehr schnell an die Grenzen gestoßen. Kein Wunder, tatsächlich ist dieses Gebiet nur recht spärlich dokumentiert, und wenn, dann auch oft nur in veralteten PHP 4-Fassungen. Auf die Gefahr hin, dass auch dieser Artikel eines Tages veraltet, versuche ich, es so allgemein wie möglich zu beschreiben, aber ab und an muss man die Fehler in der API von Zend's PHP-Core selber ein klein wenig korrigieren, und die sind immer wieder verschieden. Am Beispiel allerdings werde ich hier auf die momentane aktuelle Version PHP 5.3.x eingehen, und das unter WAP. (Windows (XP), Apache 2.2.x, PHP)

- Ein Linuxer kann es sich aber auch mal ruhig durchlesen, die Vorgehensweise ist im Prinzip die selbe, nur die Umsetzung eine andere. Bei Windows wird eine DLL rauskommen, bei Linux eine SO. -

Allerdings werde hierbei nicht erläutern, wie man nun mit C eine Extension schreibt, dafür sei auf Zend hingewiesen, denn das pure Programmieren wird sich kaum ändern und wird auch hoffentlich weiterhin ein wenig dokumentiert. Vielmehr werde ich hier das größte Problem lösen, wie man sich Visual Studio 2008 (oder allgemein auch eine andere IDE für Linux) für PHP-Extensionentwicklung einrichtet. Also, legen wir los.

Vorraussetzungen


Zu allerallererst brauchen wir natürlich ein lauffähiges PHP, damit wir unsere Extension ausprobieren können. Aber Achtung: Du solltest wissen, welche Version dein PHP hat (möglichst noch so aktuell, dass man es bei www.php.net herunterladen kann). Danach musst du in Erfahrung bringen, ob es threadsicher ist oder nicht (ts/nts). Anschließend ist es wichtig, dass du weißt, ob PHP bei dir als Apache-Modul oder als (Fast-)CGI-Modul läuft, und zu guter letzt, mit welchem Compiler dein PHP übersetzt wurde.
Nur wenn deine Extension und PHP in diesen Bedingungen identisch sind, kann PHP am Ende die DLL (SO) auch laden.

Vorweg kann ich sagen, solange Apache mit der Visual C++ Runtime Version 9 (VC9, Compiler-Umgebung für C++ unter VS 2008) unkompatibel ist, mit der aber PHP möglicherweise kompiliert ist, wirst du niemals PHP als Apache-Modul zum laufen bringen, sondern nur als CGI. Alternativ gibt es (zumindest noch für PHP 5.3.0) PHP-Kompilate, die mit der VC6 hergestellt sind. Die laufen dann auch als Apache-Modul, aber dafür kann man damit auf keinen Fall die VC9-Extensions verwenden, die man dann mit einer neueren VS 2008-Version hergestellt hat. Deshalb muss man sehen, dass man sich Visual Studio 6 irgendwo auftreibt, welches die VC6 noch hat.
Bei Linux sollte man sehen, dass Apache, PHP und die Extension mit dem selben GCC (g++) kompiliert werden, dann dürften da keine weiteren Probleme bzgl. Kompatibilität auftauchen.
Wie auch immer, ich verwende hierbei ein threadsicheres, VC9-kompiliertes, als FastCGI installiertes PHP 5.3.0, aber ich habe es auch mit der dev-Version von PHP 5.3.1 probiert und es funktionierte einwandfrei.
Es ist also zu empfehlen, die VC9-Version unter Apache als CGI zu installieren, wer eine eigene VS 2008-Extension benutzen will. Was den IIS angeht, so habe ich keine Ahnung, aber da sollte es bzgl. der VC9 keine Probleme geben.
So, wenn PHP läuft, werden wir von www.php.net auch noch den Sourcecode von PHP herunterladen, was logischerweise die selbe Version wie das installierte PHP haben sollte. Das sind zwar Linux-GZip-Archive (.tar.gz), aber mit guten Programmen wie WinRAR ist das kein Problem, diese unter Windows zu entpacken. Wir entpacken das ganze in irgendein Verzeichnis.
Ich werde folgend das Verzeichnis PHPSource nennen. In PHPSource/ext/ liegen die Sourcecodes sämtlicher PHP-Extensions. Hierhin werden wir auch unsere PHP-Extension einlagern und kompilieren, also ist der Pfad grob: PHPSource/ext/MyExt/

Einrichten von Visual Studio 2008


Vorweg: auch, wenn es hier um VS geht, ist das ändern der API, das definieren von Präprozessor-Konstanten und das Linken externer Bibliotheken für alle IDEs und Betriebssystemen identisch, nur ist die Umsetzung eine andere.

Projekt einrichten


Starten wir also Visual Studio und bereiten es für PHP vor. Wir erstellen ein neues Windows-Projekt, (nicht Konsole, sondern normale Win32-Anwendung).

Anschließend wählen wir "Leeres Projekt" und klicken auf "DLL".

Ist das Projekt fertig, legen wir sogleich eine C-Datei an.

Namenskonventionen


Wichtig: Der Linker hat eine bestimmte Namenskonvention, die mit der des Compilers übereinstimmen sollte. Der Compiler erzeugt eine .obj (.o unter Linux), in der Symbole von z.B. Funktionen definiert sind. Anhand dieser Symbole weiß anschließend der Linker, nach welcher Funktion er in anderen .obj, .lib usw. suchen soll. Die php5ts.lib von PHP wurde mit den C-Namenskonventionen kompiliert, also muss unser Compiler die ebenfalls benutzen. Verwendet man stattdessen die Namenskonvention von C++, wird es Linker-Fehlermeldungen geben.
Unter Visual Studio muss man die Datei einfach mit der Endung ".c" statt ".cpp" versehen, dann weiß der Compiler, dass er die C-Namenskonventionen von VS aufrufen soll. Wie es bei GCC ist, muss man mal selber nachschauen.

Einrichten der Pfade und Konstanten für den Compiler


Also, wir erstellen eine main.c:


Diese Datei befüllen wir später.
Jetzt bereiten wir ein paar Include-Pfade vor, damit der Compiler die Headerdateien findet. Wie die Pfade jetzt genau aussehen, das ist dir überlassen (relativ, absolut usw.), ich führe jetzt nur auf, was wir wirklich benötigen. Wir machen einen Rechtsklick auf unser "Projekt ---> Eigenschaften ---> C/C++ ---> Allgemein" und schreiben unsere Pfade in das Feld "Weitere Includeverzeichnisse", damit wir beim einbinden der Header-Dateien nicht dauernd einen Pfad angeben müssen.
Die Pfade lauten:
Code:
PHPSource/
PHPSource/main
PHPSource/win32 (nur für Windows)
PHPSource/TSRM
PHPSource/Zend
PHPSource/ext/standard


Achtung: Ich mache die Einstellungen für die Konfiguration Release, nicht Debug!

So, jetzt werden wir ein paar Präprozessor-Konstanten definieren.

(Für die Linuxer, ihr könnt, wenn eure IDE das nicht kann, eine neue Header-Datei erstellen und programmiert einfach die unten genannten Einstellungen mit "#define" in die Headerdatei. Die muss dann mit #include als allererste Datei eingebunden werden!)

Die Einstellungen definieren wir unter "C/C++ ----> Präprozessor ---> Präprozessordefinitionen". Da sind schon ein paar Einstellungen drin, und wir ergänzen:
Code:
ZEND_DEBUG=0
COMPILE_DL_MYEXT_MOD
ZTS=1
ZEND_WIN32
PHP_WIN32

Bei "COMPILE_DL_MYEXT_MOD" kann man seinen eigenen Namen verwenden, das _MOD sollte am Ende aber bleiben und man sollte sich merken, wie die Konstante heißt, da man sie beim Programmieren später braucht. Wenn man das (z.B. für Linux) lieber ein einer Header-Datei macht, sähe das so aus:
Code (C):
#define ZEND_DEBUG 0
#define COMPILE_DL_MYEXT_MOD
#define ZTS 1

// Nur für Windows:
#define ZEND_WIN32
#define PHP_WIN32


Ignoriert bitte die letzte Anweisung mit PHP_COMPILER_ID auf diesem Screenshot, die ist falsch, nehmt nur das, was ich oben schriftlich hingeschrieben habe.

Standartmäßig bei VS, aber trotzdem wichtig ist, dass bei "C/C++ ---> Codegenerierung ---> Laufzeitbibliothek" eine Multithreaded-Variante gewählt ist.

Einrichten des Linkers



Jetzt müssen wir noch einen Pfad für den Linker einstellen, den Pfad zur PHP-Development-Bibliothek, die bei PHP 5 php5ts.lib unter Windows heißt. In ihr sind sämtliche PHP-Funktionen definiert, die der Linker am Ende zum kompilieren braucht. Sie liegt im Installationsordner von PHP, nicht im Sourcecode-Ordner. Bei mir z.B. ist es C:\php5_3_0\dev. De facto ist sie im dev-Verzeichnis. Das stellen wir bei "Linker ---> Allgemein ---> Zusätzliche Bibliotheksverzeichnisse" ein.

Jetzt linken wir noch die php5ts.lib dazu, bei "Linker ---> Eingabe ---> Zusätzliche Abhängigkeiten".


Allgemeines Testen


Was jetzt kommt, ist von PHP-Version zu PHP-Version verschieden. In PHP 5.2.10 z.B. musste ich ca. 30 Codezeilen auskommentieren, in anderen Versionen völlig neue erstellen. Deshalb kann man jetzt nur testen und versuchen, eventuelle Kompilierfehler selber zu beseitigen. Ich nutze ja PHP 5.3.x, deshalb werde ich hier auch den Fehler eklären.

Der Extension-Code


Den folgenden C-Code musst du jetzt noch nicht verstehen, du musst in nur in deine main.c einfügen und schauen, ob es alles Fehler- und Warnungsfrei kompiliert.
Code (C):
#include "php.h"
#include "info.h"

ZEND_FUNCTION(hello_world);
PHP_MINFO_FUNCTION(myextmod);

zend_function_entry myext_functions[] = {
  ZEND_FE(hello_world, NULL) {
    NULL, NULL, NULL
  }
};

zend_module_entry myextmod_module_entry = {
  STANDARD_MODULE_HEADER,
  "My Extension",
  myext_functions,
  NULL, NULL, NULL, NULL,
  PHP_MINFO(myextmod),
  NO_VERSION_YET,
  STANDARD_MODULE_PROPERTIES
};

#if COMPILE_DL_MYEXT_MOD
ZEND_GET_MODULE(myextmod)
#endif

PHP_MINFO_FUNCTION(myextmod) {
  php_info_print_table_start();
  php_info_print_table_row(2, "My Extension", "Alle Systems are running");
  php_info_print_table_end();
}

ZEND_FUNCTION(hello_world) {
  zend_printf("Hallo, Welt!");
}


Bei mir kam das in der Fehlerkonsole (STRG + ^, STRG + E):
Zitat:
Zitat von Visual Studio Fehlerkonsole
Fehler 1: fatal error C1083: Datei (Include) kann nicht geöffnet werden: "config.w32.h": No such file or directory in c:\phpsource\main\php_compat.h on line 25
Das ist natürlich nur ein Problem mit Windows, bei Linux sollte das nicht passieren.
So, irgendwoher hatte ich noch die gesuchte Datei, und sie funktioniert auf jeden Fall mit den PHP 5.3-Serien. Du musst also in PHPSource/main/ die Datei config.w32.h erstellen und sie mit folgenden Code befüllen (einfach 1 zu 1 kopieren):
Code (C):
/*
  Build Configuration Template for Win32.
  $Id: config.w32.h.in,v 1.7.2.4.2.3.2.13 2009/05/29 08:11:36 pajoye Exp $
*/


/* Define the minimum supported version */
#undef _WIN32_WINNT
#undef NTDDI_VERSION
#define _WIN32_WINNT 0x500
#define NTDDI_VERSION  _WIN32_WIN2K

/* Default PHP / PEAR directories */
#define PHP_CONFIG_FILE_PATH (getenv("SystemRoot"))?getenv("SystemRoot"):""
#define CONFIGURATION_FILE_PATH "php.ini"
#define PEAR_INSTALLDIR "@PREFIX@\\pear"
#define PHP_BINDIR "@PREFIX@"
#define PHP_DATADIR "@PREFIX@"
#define PHP_EXTENSION_DIR "@PREFIX@"
#define PHP_INCLUDE_PATH  ".;@PREFIX@\\pear"
#define PHP_LIBDIR "@PREFIX@"
#define PHP_LOCALSTATEDIR "@PREFIX@"
#define PHP_PREFIX "@PREFIX@"
#define PHP_SYSCONFDIR "@PREFIX@"

/* Enable / Disable crypt() function (default: enabled) */
#define HAVE_CRYPT 1
#define PHP_STD_DES_CRYPT 1
#define PHP_EXT_DES_CRYPT 1
#define PHP_MD5_CRYPT 1
#define PHP_BLOWFISH_CRYPT 1

/* PHP Runtime Configuration */
#define PHP_URL_FOPEN 1
#define PHP_SAFE_MODE 0
#define MAGIC_QUOTES 0
#define USE_CONFIG_FILE 1
#define DEFAULT_SHORT_OPEN_TAG "1"

/* Platform-Specific Configuration. Should not be changed. */
#define PHP_SIGCHILD 0
#define HAVE_LIBBIND 1
#define HAVE_GETSERVBYNAME 1
#define HAVE_GETSERVBYPORT 1
#define HAVE_GETPROTOBYNAME 1
#define HAVE_GETPROTOBYNUMBER 1
#define HAVE_GETHOSTNAME 1
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#define HAVE_ERRMSG_H 0
#undef HAVE_ADABAS
#undef HAVE_SOLID
#undef HAVE_LINK
#undef HAVE_SYMLINK

/* its in win32/time.c */
#define HAVE_USLEEP 1
#define HAVE_NANOSLEEP 1

#define HAVE_GETHOSTNAME 1
#define HAVE_GETCWD 1
#define HAVE_POSIX_READDIR_R 1
#define NEED_ISBLANK 1
#define DISCARD_PATH 0
#undef HAVE_SETITIMER
#undef HAVE_SIGSETJMP
#undef HAVE_IODBC
#define HAVE_LIBDL 1
#define HAVE_GETTIMEOFDAY 1
#define HAVE_PUTENV 1
#define HAVE_LIMITS_H 1
#define HAVE_TZSET 1
#define HAVE_TZNAME 1
#undef HAVE_FLOCK
#define HAVE_ALLOCA 1
#undef HAVE_SYS_TIME_H
#define HAVE_SIGNAL_H 1
#undef HAVE_ST_BLKSIZE
#undef HAVE_ST_BLOCKS
#define HAVE_ST_RDEV 1
#define HAVE_UTIME_NULL 1
#define HAVE_VPRINTF 1
#define STDC_HEADERS 1
#define REGEX 1
#define HSREGEX 1
#define HAVE_GCVT 1
#define HAVE_GETLOGIN 1
#define HAVE_GETTIMEOFDAY 1
#define HAVE_MEMCPY 1
#define HAVE_MEMMOVE 1
#define HAVE_PUTENV 1
#define HAVE_REGCOMP 1
#define HAVE_SETLOCALE 1
#define HAVE_LOCALECONV 1
#define HAVE_LOCALE_H 1
#ifndef HAVE_LIBBIND
# define HAVE_SETVBUF 1
#endif
#define HAVE_SHUTDOWN 1
#define HAVE_SNPRINTF 1
#define HAVE_VSNPRINTF 1
#define HAVE_STRCASECMP 1
#define HAVE_STRDUP 1
#define HAVE_STRERROR 1
#define HAVE_STRSTR 1
#define HAVE_TEMPNAM 1
#define HAVE_UTIME 1
#undef HAVE_DIRENT_H
#define HAVE_ASSERT_H 1
#define HAVE_FCNTL_H 1
#define HAVE_GRP_H 0
#undef HAVE_PWD_H
#define HAVE_STRING_H 1
#undef HAVE_SYS_FILE_H
#undef HAVE_SYS_SOCKET_H
#undef HAVE_SYS_WAIT_H
#define HAVE_SYSLOG_H 1
#undef HAVE_UNISTD_H
#define HAVE_SYS_TYPES_H 1
#define HAVE_STDARG_H 1
#undef HAVE_ALLOCA_H
#undef HAVE_KILL
#define HAVE_GETPID 1
#define HAVE_LIBM 1
#define HAVE_CUSERID 0
#undef HAVE_RINT
#define HAVE_STRFTIME 1
#define SIZEOF_SHORT 2
/* int and long are stll 32bit in 64bit compiles */
#define SIZEOF_INT 4
#define SIZEOF_LONG 4
/* MSVC.6/NET don't allow 'long long' or know 'intmax_t' */
#define SIZEOF_LONG_LONG_INT 0
#define SIZEOF_LONG_LONG 8 /* defined as __int64 */
#define SIZEOF_INTMAX_T 0
#define ssize_t SSIZE_T
#ifdef _WIN64
# define SIZEOF_SIZE_T 8
# define SIZEOF_PTRDIFF_T 8
#else
# define SIZEOF_SIZE_T 4
# define SIZEOF_PTRDIFF_T 4
#endif
#define HAVE_FNMATCH
#define HAVE_GLOB
#define PHP_SHLIB_SUFFIX "dll"
#define HAVE_SQLDATASOURCES

/* Win32 supports strcoll */
#define HAVE_STRCOLL 1

/* Win32 supports socketpair by the emulation in win32/sockets.c */
#define HAVE_SOCKETPAIR 1
#define HAVE_SOCKLEN_T 1

/* Win32 support proc_open */
#define PHP_CAN_SUPPORT_PROC_OPEN 1

#define HAVE_MBLEN

#undef HAVE_ATOF_ACCEPTS_NAN
#undef HAVE_ATOF_ACCEPTS_INF
#define HAVE_HUGE_VAL_NAN 0

/* vs.net 2005 has a 64-bit time_t.  This will likely break
 * 3rdParty libs that were built with older compilers; switch
 * back to 32-bit */

#ifndef _WIN64
# define _USE_32BIT_TIME_T 1
#endif
#define HAVE_STDLIB_H 1

Abspeichern und neu kompilieren. So traurig wie's ist, das ist nun mal das gute alte Trial&Error-Prinzip. Wenn weitere Fehler auftauchen, muss man versuchen, die zu beheben, also scheue nicht davor, auch mal ein wenig in den Sourcecode-Dateien von PHP rumzuschnüffeln, die der Compiler als Fehlerquelle vorschlägt. Bei mir kam dann noch mal vor, dass php.h nicht gefunden wurde, in dem Fall muss man einfach auch in PHPSource/main/ eine php.h erstellen und das hier 1 zu 1 einfügen:
Code (C):
/*
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2009 The PHP Group                                |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | http://www.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Authors: Andi Gutmans <andi@zend.com>                                |
   |          Zeev Suraski <zeev@zend.com>                                |
   +----------------------------------------------------------------------+
 */


/* $Id: php.h,v 1.221.2.4.2.8.2.13 2009/06/26 15:44:19 johannes Exp $ */

#ifndef PHP_H
#define PHP_H

#ifdef HAVE_DMALLOC
#include <dmalloc.h>
#endif

#define PHP_API_VERSION 20090626
#define PHP_HAVE_STREAMS
#define YYDEBUG 0

#include "php_version.h"
#include "zend.h"
#include "zend_qsort.h"
#include "php_compat.h"

#include "zend_API.h"

#undef sprintf
#define sprintf php_sprintf

/* PHP's DEBUG value must match Zend's ZEND_DEBUG value */
#undef PHP_DEBUG
#define PHP_DEBUG ZEND_DEBUG

#ifdef PHP_WIN32
# include "tsrm_win32.h"
# include "win95nt.h"
# ifdef PHP_EXPORTS
#   define PHPAPI __declspec(dllexport)
# else
#   define PHPAPI __declspec(dllimport)
# endif
# define PHP_DIR_SEPARATOR '\\'
# define PHP_EOL "\r\n"
#else
# if defined(__GNUC__) && __GNUC__ >= 4
#   define PHPAPI __attribute__ ((visibility("default")))
# else
#   define PHPAPI
# endif

#define THREAD_LS
#define PHP_DIR_SEPARATOR '/'
#if defined(__MacOSX__)
#define PHP_EOL "\r"
#else
#define PHP_EOL "\n"
#endif
#endif

#ifdef NETWARE
/* For php_get_uname() function */
#define PHP_UNAME  "NetWare"
#define PHP_OS      PHP_UNAME
#endif

#if HAVE_ASSERT_H
#if PHP_DEBUG
#undef NDEBUG
#else
#ifndef NDEBUG
#define NDEBUG
#endif
#endif
#include <assert.h>
#else /* HAVE_ASSERT_H */
#define assert(expr) ((void) (0))
#endif /* HAVE_ASSERT_H */

#define APACHE 0

#if HAVE_UNIX_H
#include <unix.h>
#endif

#if HAVE_ALLOCA_H
#include <alloca.h>
#endif

#if HAVE_BUILD_DEFS_H
#include <build-defs.h>
#endif

/*
 * This is a fast version of strlcpy which should be used, if you
 * know the size of the destination buffer and if you know
 * the length of the source string.
 *
 * size is the allocated number of bytes of dst
 * src_size is the number of bytes excluding the NUL of src
 */


#define PHP_STRLCPY(dst, src, size, src_size) \
  {                     \
    size_t php_str_len;           \
                        \
    if (src_size >= size)         \
      php_str_len = size - 1;       \
    else                  \
      php_str_len = src_size;       \
    memcpy(dst, src, php_str_len);      \
    dst[php_str_len] = '\0';        \
  }


#ifndef HAVE_STRLCPY
BEGIN_EXTERN_C()
PHPAPI size_t php_strlcpy(char *dst, const char *src, size_t siz);
END_EXTERN_C()
#undef strlcpy
#define strlcpy php_strlcpy
#endif

#ifndef HAVE_STRLCAT
BEGIN_EXTERN_C()
PHPAPI size_t php_strlcat(char *dst, const char *src, size_t siz);
END_EXTERN_C()
#undef strlcat
#define strlcat php_strlcat
#endif

#ifndef HAVE_STRTOK_R
BEGIN_EXTERN_C()
char *strtok_r(char *s, const char *delim, char **last);
END_EXTERN_C()
#endif

#ifndef HAVE_SOCKLEN_T
typedef unsigned int socklen_t;
#endif

#define CREATE_MUTEX(a, b)
#define SET_MUTEX(a)
#define FREE_MUTEX(a)

/*
 * Then the ODBC support can use both iodbc and Solid,
 * uncomment this.
 * #define HAVE_ODBC (HAVE_IODBC|HAVE_SOLID)
 */


#include <stdlib.h>
#include <ctype.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_STDARG_H
#include <stdarg.h>
#else
# if HAVE_SYS_VARARGS_H
# include <sys/varargs.h>
# endif
#endif

#ifndef va_copy
# ifdef __va_copy
#  define va_copy(ap1, ap2)         __va_copy((ap1), (ap2))
# else
#  define va_copy(ap1, ap2)         memcpy((&ap1), (&ap2), sizeof(va_list))
# endif
#endif

#include "zend_hash.h"
#include "php3_compat.h"
#include "zend_alloc.h"
#include "zend_stack.h"

#if STDC_HEADERS
# include <string.h>
#else
# ifndef HAVE_MEMCPY
#  define memcpy(d, s, n) bcopy((s), (d), (n))
# endif
# ifndef HAVE_MEMMOVE
#  define memmove(d, s, n)  bcopy ((s), (d), (n))
# endif
#endif

#include "safe_mode.h"

#ifndef HAVE_STRERROR
char *strerror(int);
#endif

#if HAVE_PWD_H
# ifdef PHP_WIN32
#include "win32/param.h"
# else
#include <pwd.h>
#include <sys/param.h>
# endif
#endif

#if HAVE_LIMITS_H
#include <limits.h>
#endif

#ifndef LONG_MAX
#define LONG_MAX 2147483647L
#endif

#ifndef LONG_MIN
#define LONG_MIN (- LONG_MAX - 1)
#endif

#ifndef INT_MAX
#define INT_MAX 2147483647
#endif

#ifndef INT_MIN
#define INT_MIN (- INT_MAX - 1)
#endif

#define PHP_GCC_VERSION ZEND_GCC_VERSION
#define PHP_ATTRIBUTE_MALLOC ZEND_ATTRIBUTE_MALLOC
#define PHP_ATTRIBUTE_FORMAT ZEND_ATTRIBUTE_FORMAT

BEGIN_EXTERN_C()
#include "snprintf.h"
END_EXTERN_C()
#include "spprintf.h"

#define EXEC_INPUT_BUF 4096

#define PHP_MIME_TYPE "application/x-httpd-php"

/* macros */
#define STR_PRINT(str)  ((str)?(str):"")

#ifndef MAXPATHLEN
# ifdef PATH_MAX
#  define MAXPATHLEN PATH_MAX
# elif defined(MAX_PATH)
#  define MAXPATHLEN MAX_PATH
# else
#  define MAXPATHLEN 256    /* Should be safe for any weird systems that do not define it */
# endif
#endif


/* global variables */
#if !defined(PHP_WIN32)
#define PHP_SLEEP_NON_VOID
#define php_sleep sleep
extern char **environ;
#endif  /* !defined(PHP_WIN32) */

#ifdef PHP_PWRITE_64
ssize_t pwrite(int, void *, size_t, off64_t);
#endif

#ifdef PHP_PREAD_64
ssize_t pread(int, void *, size_t, off64_t);
#endif

BEGIN_EXTERN_C()
void phperror(char *error);
PHPAPI int php_write(void *buf, uint size TSRMLS_DC);
PHPAPI int php_printf(const char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, 1,
    2);
PHPAPI void php_log_err(char *log_message TSRMLS_DC);
int Debug(char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, 1, 2);
int cfgparse(void);
END_EXTERN_C()

#define php_error zend_error
#define error_handling_t zend_error_handling_t

BEGIN_EXTERN_C()
static inline ZEND_ATTRIBUTE_DEPRECATED void php_set_error_handling(error_handling_t error_handling, zend_class_entry *exception_class TSRMLS_DC)
{
  zend_replace_error_handling(error_handling, exception_class, NULL TSRMLS_CC);
}
static inline ZEND_ATTRIBUTE_DEPRECATED void php_std_error_handling() {}

PHPAPI void php_verror(const char *docref, const char *params, int type, const char *format, va_list args TSRMLS_DC) PHP_ATTRIBUTE_FORMAT(printf, 4, 0);

#ifdef ZTS
#define PHP_ATTR_FMT_OFFSET 1
#else
#define PHP_ATTR_FMT_OFFSET 0
#endif

/* PHPAPI void php_error(int type, const char *format, ...); */
PHPAPI void php_error_docref0(const char *docref TSRMLS_DC, int type, const char *format, ...)
  PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 3, PHP_ATTR_FMT_OFFSET + 4);
PHPAPI void php_error_docref1(const char *docref TSRMLS_DC, const char *param1, int type, const char *format, ...)
  PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 4, PHP_ATTR_FMT_OFFSET + 5);
PHPAPI void php_error_docref2(const char *docref TSRMLS_DC, const char *param1, const char *param2, int type, const char *format, ...)
  PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 5, PHP_ATTR_FMT_OFFSET + 6);
END_EXTERN_C()

#define php_error_docref php_error_docref0

#define zenderror phperror
#define zendlex phplex

#define phpparse zendparse
#define phprestart zendrestart
#define phpin zendin

#define php_memnstr zend_memnstr

/* functions */
BEGIN_EXTERN_C()
PHPAPI extern int (*php_register_internal_extensions_func)(TSRMLS_D);
PHPAPI int php_register_internal_extensions(TSRMLS_D);
PHPAPI int php_mergesort(void *base, size_t nmemb, register size_t size, int (*cmp)(const void *, const void * TSRMLS_DC) TSRMLS_DC);
PHPAPI void php_register_pre_request_shutdown(void (*func)(void *), void *userdata);
PHPAPI void php_com_initialize(TSRMLS_D);
END_EXTERN_C()

/* PHP-named Zend macro wrappers */
#define PHP_FN          ZEND_FN
#define PHP_MN          ZEND_MN
#define PHP_NAMED_FUNCTION    ZEND_NAMED_FUNCTION
#define PHP_FUNCTION      ZEND_FUNCTION
#define PHP_METHOD        ZEND_METHOD

#define PHP_RAW_NAMED_FE ZEND_RAW_NAMED_FE
#define PHP_NAMED_FE  ZEND_NAMED_FE
#define PHP_FE      ZEND_FE
#define PHP_DEP_FE      ZEND_DEP_FE
#define PHP_FALIAS    ZEND_FALIAS
#define PHP_DEP_FALIAS  ZEND_DEP_FALIAS
#define PHP_ME          ZEND_ME
#define PHP_MALIAS      ZEND_MALIAS
#define PHP_ABSTRACT_ME ZEND_ABSTRACT_ME
#define PHP_ME_MAPPING  ZEND_ME_MAPPING

#define PHP_MODULE_STARTUP_N  ZEND_MODULE_STARTUP_N
#define PHP_MODULE_SHUTDOWN_N ZEND_MODULE_SHUTDOWN_N
#define PHP_MODULE_ACTIVATE_N ZEND_MODULE_ACTIVATE_N
#define PHP_MODULE_DEACTIVATE_N ZEND_MODULE_DEACTIVATE_N
#define PHP_MODULE_INFO_N   ZEND_MODULE_INFO_N

#define PHP_MODULE_STARTUP_D  ZEND_MODULE_STARTUP_D
#define PHP_MODULE_SHUTDOWN_D ZEND_MODULE_SHUTDOWN_D
#define PHP_MODULE_ACTIVATE_D ZEND_MODULE_ACTIVATE_D
#define PHP_MODULE_DEACTIVATE_D ZEND_MODULE_DEACTIVATE_D
#define PHP_MODULE_INFO_D   ZEND_MODULE_INFO_D

/* Compatibility macros */
#define PHP_MINIT   ZEND_MODULE_STARTUP_N
#define PHP_MSHUTDOWN ZEND_MODULE_SHUTDOWN_N
#define PHP_RINIT   ZEND_MODULE_ACTIVATE_N
#define PHP_RSHUTDOWN ZEND_MODULE_DEACTIVATE_N
#define PHP_MINFO   ZEND_MODULE_INFO_N
#define PHP_GINIT   ZEND_GINIT
#define PHP_GSHUTDOWN ZEND_GSHUTDOWN

#define PHP_MINIT_FUNCTION    ZEND_MODULE_STARTUP_D
#define PHP_MSHUTDOWN_FUNCTION  ZEND_MODULE_SHUTDOWN_D
#define PHP_RINIT_FUNCTION    ZEND_MODULE_ACTIVATE_D
#define PHP_RSHUTDOWN_FUNCTION  ZEND_MODULE_DEACTIVATE_D
#define PHP_MINFO_FUNCTION    ZEND_MODULE_INFO_D
#define PHP_GINIT_FUNCTION    ZEND_GINIT_FUNCTION
#define PHP_GSHUTDOWN_FUNCTION  ZEND_GSHUTDOWN_FUNCTION
 
#define PHP_MODULE_GLOBALS    ZEND_MODULE_GLOBALS


/* Output support */
#include "main/php_output.h"
#define PHPWRITE(str, str_len)    php_body_write((str), (str_len) TSRMLS_CC)
#define PUTS(str)         do {      \
  const char *__str = (str);            \
  php_body_write(__str, strlen(__str) TSRMLS_CC); \
} while (0)


#define PUTC(c)           (php_body_write(&(c), 1 TSRMLS_CC), (c))
#define PHPWRITE_H(str, str_len)  php_header_write((str), (str_len) TSRMLS_CC)
#define PUTS_H(str)         do {        \
  const char *__str = (str);              \
  php_header_write(__str, strlen(__str) TSRMLS_CC); \
} while (0)


#define PUTC_H(c)         (php_header_write(&(c), 1 TSRMLS_CC), (c))

#include "php_streams.h"
#include "php_memory_streams.h"
#include "fopen_wrappers.h"


/* Virtual current working directory support */
#include "tsrm_virtual_cwd.h"

#include "zend_constants.h"

/* connection status states */
#define PHP_CONNECTION_NORMAL  0
#define PHP_CONNECTION_ABORTED 1
#define PHP_CONNECTION_TIMEOUT 2

#include "php_reentrancy.h"

/* Finding offsets of elements within structures.
 * Taken from the Apache code, which in turn, was taken from X code...
 */


#ifndef XtOffset
#if defined(CRAY) || (defined(__arm) && !(defined(LINUX) || defined(__riscos__)))
#ifdef __STDC__
#define XtOffset(p_type, field) _Offsetof(p_type, field)
#else
#ifdef CRAY2
#define XtOffset(p_type, field) \
    (sizeof(int)*((unsigned int)&(((p_type)NULL)->field)))


#else /* !CRAY2 */

#define XtOffset(p_type, field) ((unsigned int)&(((p_type)NULL)->field))

#endif /* !CRAY2 */
#endif /* __STDC__ */
#else /* ! (CRAY || __arm) */

#define XtOffset(p_type, field) \
    ((long) (((char *) (&(((p_type)NULL)->field))) - ((char *) NULL)))


#endif /* !CRAY */
#endif /* ! XtOffset */

#ifndef XtOffsetOf
#ifdef offsetof
#define XtOffsetOf(s_type, field) offsetof(s_type, field)
#else
#define XtOffsetOf(s_type, field) XtOffset(s_type*, field)
#endif
#endif /* !XtOffsetOf */

#endif

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: sw=4 ts=4 fdm=marker
 * vim<600: sw=4 ts=4
 */

Aber das passiert nicht bei jeder Version von PHP 5.3. Wenn dann mal am Ende alles zu kompilieren geht, entnehmen wir die fertige DLL (SO) aus unserem Projektverzeichnis und schieben diese in das ext-Verzeichnis von unserem PHP-Installationsverzeichnis. Jetzt stellen wir in der php.ini noch rasch ein:
Code:
; windows:
extension=myext.dll
; linux:
extension=myext.so

und starten den Apache neu. Anschließend rufen wir mal einfach eine phpinfo()-Datei auf. Bei Windows kann es jetzt vorkommen, dass eine Nachrichtenbox von der CLI sich öffnet mit folgenden, ungefähren Inhalt:
Zitat:
Zitat von PHP CLI
Warning
PHP Startup: MyExt: Unable to initialize module
Module compiled with build ID=API20090626,TS
PHP compiled with build ID=API20090626,TS,VC9
These options need to match
Wenn dieses API2009 usw. nicht übereinstimmen sollte, dann sind die PHP-Versionen vom Sourcecode und der Installation verschieden. Sollte irgendwo einmal TS und mal NTS stehen, sind sie bzgl. der Thread-Safety verschieden, was sie aber nicht sein sollten. Am Ende steht noch VC9 beim PHP-Kompilat. Aber unsere Extension ist doch auch mit VC9 gemacht. Damit diese Formalität stimmt, müssen wir das extra mit einer Präprozessor-Direktive definieren. Beim durchschnüffeln der Source habe ich folgende Datei gefunden:
PHPSource/Zend/zend_build.h
In irgendeiner Zeile steht:
Code (C):
#if defined(ZEND_WIN32) && defined(PHP_COMPILER_ID)

Und PHP_COMPILER_ID ist noch nicht definiert. Wir schreiben also das darüber:
Code (C):
#define PHP_COMPILER_ID "VC9"

Und wir kompilieren die DLL nochmal. Jetzt sollten am Ende auch alle build ID-Sachen von PHP mit der von der Extension übereinstimmen.

Abschluss


Scrollen wir jetzt etwas in der phpinfo()-Tabelle umher, finden wir den Eintrag "My Extension". Ferner noch können wir die Funktion hello_world() aufrufen, wenn das klappt, haben wir eine fertige Extension.

Zusammenfassung


Beim einrichten muss man besonders darauf achten, dass Version, Compiler, Threadsicherheit usw. übereinstimmen. Wenn nicht, wird es die CLI melden. Die Probleme in der PHP-API sind immer anders und erfordern etwas Eigenwerk, die Lösungen, die hier vorgeschlagen sind, klappen nur unter PHP 5.3.0 und 5.3.1, aber die Folgeversionen PHP 5.3.x werden sich da wohl eher nicht unterscheiden.
Bei irgendwelchen Problemen oder Tipps, bitte ich dich, hier deinen Kommentar abzugeben, da Extensionprogrammierung doch prinzipiell ein recht begehrtes Thema ist und diverse Probleme auftauchen könnten.

Links, Quellen, Empfehlungen


Für die Erarbeitung dieses Artikel und der eigenen Erfahrung habe ich folgende (nur englische) Websites benutzt, die aber das Wissen etwas fundieren. Leider sind die etwas veraltet, aber den ungefähren Weg vermitteln sie alle (mal abgesehen vom Trial&Error):
  • Wie schreibt man eine Extension? (hier wird auch auf das C-Gedöhns dann eingegangen, speziell für Windows)
  • Extensions schreiben zur Ergänzung, hier wird auch etwas mehr auf die Zend Engine eingegangen, und wie man mit C Extension schreibt. Das alles anhand von Linux, aber wegen der halbwegs plattformunabhängigen Engine-Programmierung auch für Windows sinnvoll)
  • Ein Forenthread bei Zend von mir, vielleicht antwortet da ja noch jemand.


Mitwirkende: Ad aCTa
Erstellt von Ad aCTa, 20.08.2009 am 13:26
Zuletzt bearbeitet von Ad aCTa, 20.08.2009 am 13:44
0 Kommentare , 9874 Betrachtungen

Dieser Text steht unter der GNU-Lizenz für freie Dokumentation


 

Lesezeichen

Artikel-Optionen
Ansicht

Forumregeln
Es ist Ihnen nicht erlaubt, neue Themen zu verfassen.
Es ist Ihnen nicht erlaubt, auf Beiträge zu antworten.
Es ist Ihnen nicht erlaubt, Anhänge hochzuladen.
Es ist Ihnen nicht erlaubt, Ihre Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.

Gehe zu
Ähnliche Themen
Thema Autor Forum Antworten Letzter Beitrag
Eigene Plugins/Extensions utopia PHP 9 15.07.2009 11:57
Bürosoftware entwickeln bieler Sonstiges 9 20.12.2006 10:02
Php-Webseite entwickeln Burnser PHP 4 11.09.2006 15:01
Eigene API entwickeln (SSL-Verschlüsselung) Uncle Sam PHP 2 16.10.2005 11:20
Eigene Extensions schreiben Mathias Soeken PHP 1 01.01.1970 01:00


Alle Zeitangaben in WEZ +2. Es ist jetzt 19:01 Uhr.


Powered by vBulletin® Version 3.8.8 (Deutsch)
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
Powered by NuWiki v1.3 RC1 Copyright ©2006-2007, NuHit, LLC