From 5f94bd545b4303bb642c5f5df1994ce6bb0af07b Mon Sep 17 00:00:00 2001 From: Elmar Klausmeier Date: Sat, 24 Feb 2024 22:07:48 +0100 Subject: [PATCH] Initial revision --- .gitignore | 24 +++++++ README.md | 3 + config.m4 | 40 +++++++++++ config.w32 | 2 + md4c.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 269 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 config.m4 create mode 100644 config.w32 create mode 100644 md4c.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b299e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +.libs +Makefile +Makefile.fragments +Makefile.objects +autom4te.cache +build/ +config.h +config.h.in +config.log +config.nice +config.status +configure +configure.ac +core +include +libtool +modules +run-tests.php +*.a +*.dep +*.o +*.la +*.lo + diff --git a/README.md b/README.md new file mode 100644 index 0000000..3ff89e9 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ + +See [MD4C PHP Extension](https://eklausmeier.goip.de/blog/2024/02-24-md4c-php-extension). + diff --git a/config.m4 b/config.m4 new file mode 100644 index 0000000..c03a394 --- /dev/null +++ b/config.m4 @@ -0,0 +1,40 @@ +dnl config.m4 for php-md4c extension + +PHP_ARG_WITH(md4c, [whether to enable MD4C support], +[ --with-md4c[[=DIR]] Enable MD4C support. + DIR is the path to MD4C install prefix]) + +if test "$PHP_YAML" != "no"; then + + AC_MSG_CHECKING([for md4c headers]) + for i in "$PHP_MD4C" "$prefix" /usr /usr/local; do + if test -r "$i/include/md4c-html.h"; then + PHP_MD4C_DIR=$i + AC_MSG_RESULT([found in $i]) + break + fi + done + if test -z "$PHP_MD4C_DIR"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([Please install md4c]) + fi + + PHP_ADD_INCLUDE($PHP_MD4C_DIR/include) + dnl recommended flags for compilation with gcc + dnl CFLAGS="$CFLAGS -Wall -fno-strict-aliasing" + + export OLD_CPPFLAGS="$CPPFLAGS" + export CPPFLAGS="$CPPFLAGS $INCLUDES -DHAVE_MD4C" + AC_CHECK_HEADERS([md4c.h md4c-html.h], [], AC_MSG_ERROR(['md4c.h' header not found])) + #AC_CHECK_HEADER([md4c-html.h], [], AC_MSG_ERROR(['md4c-html.h' header not found])) + PHP_SUBST(MD4C_SHARED_LIBADD) + + PHP_ADD_LIBRARY_WITH_PATH(md4c, $PHP_MD4C_DIR/$PHP_LIBDIR, MD4C_SHARED_LIBADD) + PHP_ADD_LIBRARY_WITH_PATH(md4c-html, $PHP_MD4C_DIR/$PHP_LIBDIR, MD4C_SHARED_LIBADD) + export CPPFLAGS="$OLD_CPPFLAGS" + + PHP_SUBST(MD4C_SHARED_LIBADD) + AC_DEFINE(HAVE_MD4C, 1, [ ]) + PHP_NEW_EXTENSION(md4c, md4c.c, $ext_shared) +fi + diff --git a/config.w32 b/config.w32 new file mode 100644 index 0000000..6088434 --- /dev/null +++ b/config.w32 @@ -0,0 +1,2 @@ +ARG_ENABLE('php-md4c', 'test support', 'no'); + diff --git a/md4c.c b/md4c.c new file mode 100644 index 0000000..b56a46d --- /dev/null +++ b/md4c.c @@ -0,0 +1,200 @@ +/* MD4C extension for PHP: Markdown to HTML conversion + + Info on PHP extension writing: + 1. Sara Golemon: Extending and Embedding PHP, Sams Publishing, 2006, xx+410 p. + 2. https://www.phpinternalsbook.com/php7/extensions_design/zend_extensions.html + 3. https://github.com/dstogov/php-extension + + Elmar Klausmeier, 19-Feb-2024: Started with FFI C module and converted memory allocation +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + + +//ZEND_DECLARE_MODULE_GLOBALS(php_md4c) // no globals + + +/* {{{ void md4c_test() + */ +PHP_FUNCTION(md4c_test) { + ZEND_PARSE_PARAMETERS_NONE(); + + php_printf("The extension %s is loaded and working!\r\n", "md4c"); +} +/* }}} */ + + + +struct membuffer { + char* data; + size_t asize; // allocated size = max usable size + size_t size; // current size +}; + + + +static void membuf_init(struct membuffer* buf, MD_SIZE new_asize) { + buf->size = 0; + buf->asize = new_asize; + if ((buf->data = safe_pemalloc(buf->asize,sizeof(char),0,1)) == NULL) + php_error_docref(NULL, E_ERROR, "php-md4c.c: membuf_init: safe_pemalloc() failed with asize=%ld.\n",(long)buf->asize); +} + + + +static void membuf_grow(struct membuffer* buf, size_t new_asize) { + buf->data = safe_perealloc(buf->data, sizeof(char*), new_asize, 0, 1); + if (buf->data == NULL) + php_error_docref(NULL, E_ERROR, "php-md4c.c: membuf_grow: realloc() failed, new_asize=%ld.\n",(long)new_asize); + buf->asize = new_asize; +} + + + +static void membuf_append(struct membuffer* buf, const char* data, MD_SIZE size) { + if (buf->asize < buf->size + size) + membuf_grow(buf, buf->size + buf->size / 2 + size); + memcpy(buf->data + buf->size, data, size); + buf->size += size; +} + + + +static void process_output(const MD_CHAR* text, MD_SIZE size, void* userdata) { + membuf_append((struct membuffer*) userdata, text, size); +} + + + +static struct membuffer mbuf = { NULL, 0, 0 }; + + + +/* {{{ string md4c_toHtml( string $markdown, [ int $flag ] ) + */ +PHP_FUNCTION(md4c_toHtml) { // return HTML string + char *markdown; + size_t markdown_len; + int ret; + long flag = MD_DIALECT_GITHUB | MD_FLAG_NOINDENTEDCODEBLOCKS; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STRING(markdown, markdown_len) + Z_PARAM_OPTIONAL Z_PARAM_LONG(flag) + ZEND_PARSE_PARAMETERS_END(); + + if (mbuf.asize == 0) membuf_init(&mbuf,16777216); // =16MB + + mbuf.size = 0; // prepare for next call + ret = md_html(markdown, markdown_len, process_output, + &mbuf, (MD_SIZE)flag, 0); + membuf_append(&mbuf,"\0",1); // make it a null-terminated C string, so PHP can deduce length + if (ret < 0) { + RETVAL_STRINGL("
- - - Error in Markdown - - -
\n",sizeof("
- - - Error in Markdown - - -
\n")); + } else { + RETVAL_STRING(estrndup(mbuf.data,mbuf.size)); + } +} +/* }}}*/ + + + +//static PHP_GINIT_FUNCTION(md4c) { +//#if defined(COMPILE_DL_BCMATH) && defined(ZTS) +// ZEND_TSRMLS_CACHE_UPDATE(); +//#endif + // do absolutely nothing +//} + + + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(md4c) { // module initialization + //REGISTER_INI_ENTRIES(); + //php_printf("In PHP_MINIT_FUNCTION(md4c): module initialization\n"); + + return SUCCESS; +} +/* }}} */ + + + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(md4c) { // module shutdown + if (mbuf.data) pefree(mbuf.data,1); + return SUCCESS; +} +/* }}} */ + + + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(md4c) { + php_info_print_table_start(); + php_info_print_table_row(2, "MD4C", "enabled"); + php_info_print_table_row(2, "PHP-MD4C version", "1.0"); + php_info_print_table_row(2, "MD4C version", "0.5.2"); + php_info_print_table_end(); +} +/* }}} */ + + + +/* {{{ arginfo + */ +ZEND_BEGIN_ARG_INFO(arginfo_md4c_test, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_md4c_toHtml, 1) + ZEND_ARG_INFO(0, str) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, flag, "MD_DIALECT_GITHUB | MD_FLAG_NOINDENTEDCODEBLOCKS") +ZEND_END_ARG_INFO() +/* }}} */ + + + +/* {{{ test_functions[] + */ +static const zend_function_entry php_md4c_functions[] = { + PHP_FE(md4c_test, arginfo_md4c_test) + PHP_FE(md4c_toHtml, arginfo_md4c_toHtml) + PHP_FE_END +}; +/* }}} */ + + + +/* {{{ md4c_module_entry + */ +zend_module_entry md4c_module_entry = { + STANDARD_MODULE_HEADER, + "md4c", // Extension name + php_md4c_functions, // zend_function_entry + NULL, //PHP_MINIT(md4c), // PHP_MINIT - Module initialization + PHP_MSHUTDOWN(md4c), // PHP_MSHUTDOWN - Module shutdown + NULL, // PHP_RINIT - Request initialization + NULL, // PHP_RSHUTDOWN - Request shutdown + PHP_MINFO(md4c), // PHP_MINFO - Module info + "1.0", // Version + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + + + +#ifdef COMPILE_DL_TEST +# ifdef ZTS +ZEND_TSRMLS_CACHE_DEFINE() +# endif +#endif +ZEND_GET_MODULE(md4c) +