Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[WIP] an approach to embedding main() into sapi/embed from php code #19707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
krakjoe wants to merge 1 commit into php:master
base: master
Choose a base branch
Loading
from krakjoe:krakjoe/embed-xxd
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 65 additions & 2 deletions sapi/embed/config.m4
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,13 +1,36 @@
PHP_ARG_ENABLE([embed],,
[AS_HELP_STRING([[--enable-embed[=TYPE]]],
[Enable building of embedded SAPI library TYPE is either
'shared' or 'static'. [TYPE=shared]])],
'shared' or 'static' or 'main'. [TYPE=shared]])],
[no],
[no])

PHP_ARG_WITH([embed-main],,
[AS_HELP_STRING([[--enable-embed-main[=FILE]]],
[Enable building of embedded SAPI as a program with embedded main
[TYPE=no]])],
[no],
[no])

PHP_ARG_WITH([embed-link],,
[AS_HELP_STRING([[--enable-embed-link[=FILE]]],
[Enable creation of symlink to embedded code
[TYPE=no]])],
[no],
[no])

AC_MSG_CHECKING([for embedded SAPI library support])

if test "$PHP_EMBED" != "no"; then
if test "$PHP_EMBED" != "no" || test "$PHP_EMBED_MAIN" != "no"; then

if test "$PHP_EMBED" == "main"; then
if test "$PHP_EMBED_MAIN" == "no"; then
AC_MSG_ERROR([--with-embed-main is required for --enable-embed=main])
fi
elif test "$PHP_EMBED_MAIN" != "no"; then
PHP_EMBED=main
fi

AS_CASE([$PHP_EMBED],
[yes|shared], [
LIBPHP_CFLAGS="-shared"
Expand All @@ -22,6 +45,46 @@ if test "$PHP_EMBED" != "no"; then
PHP_EMBED_TYPE=static
INSTALL_IT="\$(mkinstalldirs) \$(INSTALL_ROOT)\$(orig_libdir); \$(INSTALL) -m 0644 $SAPI_STATIC \$(INSTALL_ROOT)\$(orig_libdir)"
],
[main], [
AC_MSG_RESULT([main])
AC_PATH_PROG([XXD], [xxd])
if test ! -x "$XXD"; then
AC_MSG_ERROR([xxd not found, required for --with-embed-main])
fi
AC_MSG_CHECKING([for access to $PHP_EMBED_MAIN])
if test ! -f "$PHP_EMBED_MAIN"; then
AC_MSG_ERROR([--with-embed-main argument "$PHP_EMBED_MAIN" not found])
else
AC_MSG_RESULT([ok])
fi

AC_MSG_CHECKING([generating sapi/embed/php_embed_main.h])
$XXD -i -n php_embed_main "$PHP_EMBED_MAIN" sapi/embed/php_embed_main.h
if test $? -ne 0; then
AC_MSG_ERROR([failed])
else
AC_MSG_RESULT([ok])
fi

AC_MSG_CHECKING([proceeding to final configure of embed])
LIBPHP_CFLAGS="-static"
PHP_EMBED_TYPE=static
INSTALL_IT="\
\$(mkinstalldirs) \$(INSTALL_ROOT)\$(orig_libdir); \
\$(INSTALL) -m 0644 \$(SAPI_STATIC) \$(INSTALL_ROOT)\$(orig_libdir); \
\$(mkinstalldirs) \$(INSTALL_ROOT)\$(bindir); \
\$(LIBTOOL) --tag=CC --mode=compile \$(CC) \
\$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(INCLUDES) -DHAVE_EMBED_MAIN \
-c sapi/embed/php_embed.c -o sapi/embed/php_embed_main.lo; \
\$(LIBTOOL) --tag=CC --mode=link \$(CC) \
-export-dynamic \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \
sapi/embed/php_embed_main.lo \
\$(PHP_GLOBAL_OBJS) \$(PHP_SAPI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) \
-o \$(INSTALL_ROOT)\$(bindir)/php-embed-main"
if test "$PHP_EMBED_LINK" != "no"; then
AC_DEFINE_UNQUOTED([PHP_EMBED_LINK], ["$PHP_EMBED_LINK"], [PHP_EMBED_LINK])
Copy link
Member Author

@krakjoe krakjoe Sep 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is yes/empty we could use basename of PHP_EMBED_MAIN, this will normally be correct.

fi
],
[PHP_EMBED_TYPE=])

AS_VAR_IF([PHP_EMBED_TYPE],, [AC_MSG_RESULT([no])], [
Expand Down
152 changes: 151 additions & 1 deletion sapi/embed/php_embed.c
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <fcntl.h>
#endif

#ifndef HAVE_EMBED_MAIN
static const char HARDCODED_INI[] =
"html_errors=0\n"
"register_argc_argv=1\n"
Expand Down Expand Up @@ -114,6 +115,8 @@ static void php_embed_log_message(const char *message, int syslog_type_int)
static void php_embed_register_variables(zval *track_vars_array)
{
php_import_environment_variables(track_vars_array);

php_register_variable("PHP_SELF", "-", track_vars_array);
}

/* Module initialization (MINIT) */
Expand Down Expand Up @@ -244,7 +247,6 @@ EMBED_SAPI_API int php_embed_init(int argc, char **argv)

SG(headers_sent) = 1;
SG(request_info).no_headers = 1;
php_register_variable("PHP_SELF", "-", NULL);
Copy link
Member Author

@krakjoe krakjoe Sep 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not exactly sure why this is here, it doesn't look correct ... I moved to register_server_variables because that's idiomatic, and allows consumers to control this behavior appropriately


return SUCCESS;
}
Expand All @@ -264,3 +266,151 @@ EMBED_SAPI_API void php_embed_shutdown(void)
tsrm_shutdown();
#endif
}
#endif

#ifdef HAVE_EMBED_MAIN
# include <sys/mman.h>
# include "php_embed_main.h"

static char _php_embed_main_path_[MAXPATHLEN];

static php_stream *s_in_process = NULL;

static int php_embed_main_streams(void) {
Copy link
Member Author

@krakjoe krakjoe Sep 6, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lifted from cli, possibly this should be exported API function in main ...

php_stream *s_in, *s_out, *s_err;
php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL;
zend_constant ic, oc, ec;

s_in = php_stream_open_wrapper_ex("php://stdin", "rb", 0, NULL, sc_in);
s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out);
s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err);

if (s_in) s_in->flags |= PHP_STREAM_FLAG_NO_RSCR_DTOR_CLOSE;
if (s_out) s_out->flags |= PHP_STREAM_FLAG_NO_RSCR_DTOR_CLOSE;
if (s_err) s_err->flags |= PHP_STREAM_FLAG_NO_RSCR_DTOR_CLOSE;

if (s_in==NULL || s_out==NULL || s_err==NULL) {
if (s_in) php_stream_close(s_in);
if (s_out) php_stream_close(s_out);
if (s_err) php_stream_close(s_err);
return FAILURE;
}

s_in_process = s_in;

php_stream_to_zval(s_in, &ic.value);
php_stream_to_zval(s_out, &oc.value);
php_stream_to_zval(s_err, &ec.value);

Z_CONSTANT_FLAGS(ic.value) = 0;
ic.name = zend_string_init_interned("STDIN", sizeof("STDIN")-1, 0);
zend_register_constant(&ic);

Z_CONSTANT_FLAGS(oc.value) = 0;
oc.name = zend_string_init_interned("STDOUT", sizeof("STDOUT")-1, 0);
zend_register_constant(&oc);

Z_CONSTANT_FLAGS(ec.value) = 0;
ec.name = zend_string_init_interned("STDERR", sizeof("STDERR")-1, 0);
zend_register_constant(&ec);

return SUCCESS;
}

static void php_embed_main_variables(zval *track_vars_array)
{
php_import_environment_variables(track_vars_array);

php_register_variable("PHP_SELF",
_php_embed_main_path_, track_vars_array);
}

static int php_embed_main_enter(void) {
int fd;
snprintf(_php_embed_main_path_,
MAXPATHLEN, "/tmp/php_embed_main.XXXXXX");
fd = mkstemp(_php_embed_main_path_);
if (fd == FAILURE) {
fprintf(stderr,
"php_embed_main can not be initialized "
"at /tmp/php_embed_main.*: %d %s\n",
errno, strerror(errno));
return FAILURE;
}

if (write(fd, php_embed_main, php_embed_main_len) == FAILURE) {
fprintf(stderr,
"php_embed_main can not be written "
"at %s: %d %s\n",
_php_embed_main_path_, errno, strerror(errno));
return FAILURE;
}
lseek(fd, 0, SEEK_SET);
close(fd);

#ifdef PHP_EMBED_LINK
if (symlink(_php_embed_main_path_, PHP_EMBED_LINK) != SUCCESS) {
fprintf(stderr,
"php_embed_main link can not be initialized at %s: %d %s\n",
PHP_EMBED_LINK,
errno, strerror(errno));
return FAILURE;
}
strcpy(_php_embed_main_path_, PHP_EMBED_LINK);
#endif

php_embed_module.register_server_variables = php_embed_main_variables;

return SUCCESS;
}

static int php_embed_main_execute(void) {
int status = php_embed_main_streams();

if (status != SUCCESS) {
return 1;
}

zend_file_handle fh;
zend_stream_init_filename(&fh,
_php_embed_main_path_);
if (!php_execute_script(&fh)) {
if (EG(exit_status) == SUCCESS) {
status = 1;
}
}
zend_destroy_file_handle(&fh);

if (status == FAILURE) {
status = EG(exit_status);
}

return status;
}

static void php_embed_main_leave(void) {
unlink(_php_embed_main_path_);
#ifdef PHP_EMBED_LINK
unlink(PHP_EMBED_LINK);
#endif
}

int main(int argc, char *argv[])
{
int status = FAILURE;

if (php_embed_main_enter() != SUCCESS) {
return 1;
}

PHP_EMBED_START_BLOCK(argc, argv)

status = php_embed_main_execute();

PHP_EMBED_END_BLOCK()

php_embed_main_leave();

return status;
}
#endif

AltStyle によって変換されたページ (->オリジナル) /