Hello world - in C!

Our extension does nothing right now, so what we're going to do is add a function to it, called hello_print(), that prints "Hello, world!" to the screen. This requires looking at both the hello.c and php_hello.h files - we're done with config.m4 for now.

In order that you be able to compare your version against mine, here's how your hello.c file should look:

/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2004 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.0 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_0.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.               |
  +----------------------------------------------------------------------+
  | Author:                                                              |
  +----------------------------------------------------------------------+
*/

/* $Id: header,v 1.15 2004/01/08 16:46:52 sniper Exp $ */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_hello.h"

/* If you declare any globals in php_hello.h uncomment this:
ZEND_DECLARE_MODULE_GLOBALS(hello)
*/

/* True global resources - no need for thread safety here */
static int le_hello;

/* {{{ hello_functions[]
*
* Every user visible function must have an entry in hello_functions[].
*/
function_entry hello_functions[] = {
    PHP_FE(confirm_hello_compiled,    NULL)        /* For testing, remove later. */

///////////////////////////////////////////////
/////////// CHANGE 2 GOES HERE ////////////////
///////////////////////////////////////////////

    {NULL, NULL, NULL}    /* Must be the last line in hello_functions[] */
};
/* }}} */

/* {{{ hello_module_entry
*/
zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    "hello",
    hello_functions,
    PHP_MINIT(hello),
    PHP_MSHUTDOWN(hello),
    PHP_RINIT(hello),        /* Replace with NULL if there's nothing to do at request start */
    PHP_RSHUTDOWN(hello),    /* Replace with NULL if there's nothing to do at request end */
    PHP_MINFO(hello),
#if ZEND_MODULE_API_NO >= 20010901
    "0.1", /* Replace with version number for your extension */
#endif
    STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif

/* {{{ PHP_INI
*/
/* Remove comments and fill if you need to have entries in php.ini
PHP_INI_BEGIN()
    STD_PHP_INI_ENTRY("hello.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_hello_globals, hello_globals)
    STD_PHP_INI_ENTRY("hello.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_hello_globals, hello_globals)
PHP_INI_END()
*/
/* }}} */

/* {{{ php_hello_init_globals
*/
/* Uncomment this function if you have INI entries
static void php_hello_init_globals(zend_hello_globals *hello_globals)
{
    hello_globals->global_value = 0;
    hello_globals->global_string = NULL;
}
*/
/* }}} */

/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(hello)
{
    /* If you have INI entries, uncomment these lines
    ZEND_INIT_MODULE_GLOBALS(hello, php_hello_init_globals, NULL);
    REGISTER_INI_ENTRIES();
    */
    return SUCCESS;
}
/* }}} */

/* {{{ PHP_MSHUTDOWN_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION(hello)
{
    /* uncomment this line if you have INI entries
    UNREGISTER_INI_ENTRIES();
    */
    return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request start */
/* {{{ PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION(hello)
{
    return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request end */
/* {{{ PHP_RSHUTDOWN_FUNCTION
*/
PHP_RSHUTDOWN_FUNCTION(hello)
{
    return SUCCESS;
}
/* }}} */

/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(hello)
{
    php_info_print_table_start();
    php_info_print_table_header(2, "hello support", "enabled");
    php_info_print_table_end();

    /* Remove comments if you have entries in php.ini
    DISPLAY_INI_ENTRIES();
    */
}
/* }}} */


/* Remove the following function when you have succesfully modified config.m4
   so that your module can be compiled into PHP, it exists only for testing
   purposes. */

/* Every user-visible function in PHP should document itself in the source */
/* {{{ proto string confirm_hello_compiled(string arg)
   Return a string to confirm that the module is compiled in */
PHP_FUNCTION(confirm_hello_compiled)
{
    char *arg = NULL;
    int arg_len, len;
    char string[256];

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
        return;
    }

    len = sprintf(string, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "hello", arg);
    RETURN_STRINGL(string, len, 1);
}
/* }}} */
/* The previous line is meant for vim and emacs, so it can correctly fold and
   unfold functions in source code. See the corresponding marks just before
   function definition, where the functions purpose is also documented. Please
   follow this convention for the convenience of others editing your code.
*/

///////////////////////////////////////////////
/////////// CHANGE 3 GOES HERE ////////////////
///////////////////////////////////////////////



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

And here's php_hello.h:

/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2004 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.0 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_0.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.               |
  +----------------------------------------------------------------------+
  | Author:                                                              |
  +----------------------------------------------------------------------+
*/

/* $Id: header,v 1.15 2004/01/08 16:46:52 sniper Exp $ */

#ifndef PHP_HELLO_H
#define PHP_HELLO_H

extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry

#ifdef PHP_WIN32
#define PHP_HELLO_API __declspec(dllexport)
#else
#define PHP_HELLO_API
#endif

#ifdef ZTS
#include "TSRM.h"
#endif

PHP_MINIT_FUNCTION(hello);
PHP_MSHUTDOWN_FUNCTION(hello);
PHP_RINIT_FUNCTION(hello);
PHP_RSHUTDOWN_FUNCTION(hello);
PHP_MINFO_FUNCTION(hello);

PHP_FUNCTION(confirm_hello_compiled);    /* For testing, remove later. */

///////////////////////////////////////////////
/////////// CHANGE 1 GOES HERE ////////////////
///////////////////////////////////////////////

/*
      Declare any global variables you may need between the BEGIN
    and END macros here:     

ZEND_BEGIN_MODULE_GLOBALS(hello)
    long  global_value;
    char *global_string;
ZEND_END_MODULE_GLOBALS(hello)
*/

/* In every utility function you add that needs to use variables
   in php_hello_globals, call TSRMLS_FETCH(); after declaring other
   variables used by that function, or better yet, pass in TSRMLS_CC
   after the last function argument and declare your utility function
   with TSRMLS_DC after the last declared argument.  Always refer to
   the globals in your function as HELLO_G(variable).  You are
   encouraged to rename these macros something shorter, see
   examples in any other php module directory.
*/

#ifdef ZTS
#define HELLO_G(v) TSRMG(hello_globals_id, zend_hello_globals *, v)
#else
#define HELLO_G(v) (hello_globals.v)
#endif

#endif    /* PHP_HELLO_H */


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

The exact contents of those files are likely to change from release to release, so don't be surprised if yours a little different! You'll note that I've inserted comments in there pointing out numbered steps: please don't insert these in your own code; they are there just to help point out where changes in the code should be made.

The first change is in php_hello.h, and we need to add the declaration of our hello_print() function. This is done with the PHP_FUNCTION macro, so place this line of code underneath the existing declaration (printed here also, so you can spot it) for hello_print() in php_hello.h:

PHP_FUNCTION(confirm_hello_compiled);   /* For testing, remove later. */
PHP_FUNCTION(hello_print);

The second change takes place in hello.c, near the "function_entry hello_functions[]" line. What we need to do here is declare the hello_print() function again by adding a line after the existing PHP_FE() line. Here's how it should look with the modification - again, I've included the code around the change so you can get some idea of where it is.

function_entry hello_functions[] = {
    PHP_FE(confirm_hello_compiled,  NULL)       /* For testing, remove later. */
    PHP_FE(hello_print, NULL)
    {NULL, NULL, NULL}  /* Must be the last line in hello_functions[] */
};

The last change is implementing the actual function itself. For now we're going to write it so that it accepts no parameters and simply prints out "Hello, world!". The easiest way to explain it is just to dump the code on you, then break it down. So, here goes:

/* {{{ proto void hello_print(void)
*   Print a message to show how much PHP extensions rock */
PHP_FUNCTION(hello_print)
{
    php_printf("Hello, world!\n");
}
/* }}} */

That should be placed where the third changed is marked in the code - just after the confirm_hello_compiled() function.

The first two lines and the last line are there for text editors and auto-documentation. The {{{ and }}} parts are where Unix editors like Vim and Emacs will fold the function (hide it until you request it to be opened, for extra readability). Having the prototype and comment in there means the PHP manual has at least some sort of reference, even if you haven't written any documentation. It's good practice to keep all this in your own functions.

The function kicks off with PHP_FUNCTION(hello_print), and PHP_FUNCTION() is another macro inside PHP. You'll find that PHP makes heavy use of macros, and quite rightly: it makes coding easier (although potentially a little harder to debug!) and certainly easier to read. The main reason for this macro is so that PHP can mangle your function name a little to avoid conflicts, and also to ensure it accepts the proper variables. In PHP's case, this is currently just the function return value - don't worry about it just yet.

Inside the function there's just one line, a call to php_printf(). If you've used printf() in C or PHP you'll know it just prints to the screen, and this one is only different in that it "prints" to the PHP output system - it might end up in a web browser or wherever. As with the C and PHP versions of this function, you just provide a text string and it gets printed out - perfect for our needs.

That's it! Go to the root of the PHP source directory, then type "make" then "make install-cli" to try it out. Running a simple script like this should test it out:

<?php
    hello_print();
?>

Author's Note: It's good practice to namespace your function calls. That is, rather than just hello() for a function name, hello_print() is much better - say what extension it comes from first, then an underscore, followed by what the actual function does.

 

Want to learn PHP 7?

Hacking with PHP has been fully updated for PHP 7, and is now available as a downloadable PDF. Get over 1200 pages of hands-on PHP learning today!

If this was helpful, please take a moment to tell others about Hacking with PHP by tweeting about it!

Next chapter: C Hello World v2 >>

Previous chapter: First steps

Jump to:

 

Home: Table of Contents

Copyright ©2015 Paul Hudson. Follow me: @twostraws.