/* * Compile this file together with the ph7 engine source code to generate * the executable. For example: * gcc -W -Wall -O6 -o ph7_test ph7_func_intro.c ph7.c */ /* * This simple program is a quick introduction to the foreign functions and their related * interfaces. * For an introductory course to the [ph7_create_function()] interface and the foreign function * mechanism in general, please refer to: * http://ph7.symisc.net/func_intro.html */ /* $SymiscID: ph7_func_intro.c v2.1 Win7 2012-09-06 23:30 stable $ */ /* * Make sure you have the latest release of the PH7 engine * from: * http://ph7.symisc.net/downloads.html * Make sure this header file is available. */ #include "ph7.h" /* * int shift_func(int $num) * Right shift a number by one and return the result. * Description * Our first function perform a simple right shift operation on a given number * and return that number shifted by one. * This function expects a single parameter which must be numeric (either integer or float * or a string that looks like a number). * Parameter * $num * Number to shift by one. * Return value * Integer: Given number shifted by one. * Usage example: * */ int shift_func( ph7_context *pCtx, /* Call Context */ int argc, /* Total number of arguments passed to the function */ ph7_value **argv /* Array of function arguments */ ) { int num; /* Make sure there is at least one argument and is of the * expected type [i.e: numeric]. */ if( argc < 1 || !ph7_value_is_numeric(argv[0]) ){ /* * Missing/Invalid argument, throw a warning and return FALSE. * Note that you do not need to log the function name, PH7 will * automatically append the function name for you. */ ph7_context_throw_error(pCtx, PH7_CTX_WARNING, "Missing numeric argument"); /* Return false */ ph7_result_bool(pCtx, 0); return PH7_OK; } /* Extract the number */ num = ph7_value_to_int(argv[0]); /* Shift by 1 */ num <<= 1; /* Return the new value */ ph7_result_int(pCtx, num); /* All done */ return PH7_OK; } #include /* * string date_func(void) * Return the current system date. * Description * Our second function does not expects arguments and return the * current system date. * Parameter * None * Return value * String: Current system date. * Usage example * */ int date_func( ph7_context *pCtx, /* Call Context */ int argc, /* Total number of arguments passed to the function */ ph7_value **argv /* Array of function arguments*/ ){ time_t tt; struct tm *pNow; /* Get the current time */ time(&tt); pNow = localtime(&tt); /* * Return the current date. */ ph7_result_string_format(pCtx, "%04d-%02d-%02d %02d:%02d:%02d", /* printf() style format */ pNow->tm_year + 1900, /* Year */ pNow->tm_mday, /* Day of the month */ pNow->tm_mon + 1, /* Month number */ pNow->tm_hour, /* Hour */ pNow->tm_min, /* Minutes */ pNow->tm_sec /* Seconds */ ); /* All done */ return PH7_OK; } /* * int64 sum_func(int $arg1, int $arg2, int $arg3, ...) * Return the sum of the given arguments. * Description * This function expects a variable number of arguments which must be of type * numeric (either integer or float or a string that looks like a number) and * returns the sum of the given numbers. * Parameter * int $n1, n2, ... (Variable number of arguments) * Return value * Integer64: Sum of the given numbers. * Usage example * */ int sum_func(ph7_context *pCtx, int argc, ph7_value **argv) { ph7_int64 iTotal = 0; /* Counter */ int i; if( argc < 1 ){ /* * Missing arguments, throw a notice and return NULL. * Note that you do not need to log the function name, PH7 will * automatically append the function name for you. */ ph7_context_throw_error(pCtx, PH7_CTX_NOTICE, "Missing function arguments $arg1, $arg2, .."); /* Return null */ ph7_result_null(pCtx); return PH7_OK; } /* Sum the arguments */ for( i = 0; i < argc ; i++ ){ ph7_value *pVal = argv[i]; ph7_int64 n; /* Make sure we are dealing with a numeric argument */ if( !ph7_value_is_numeric(pVal) ){ /* Throw a notice and continue */ ph7_context_throw_error_format(pCtx, PH7_CTX_NOTICE, "Arg[%d]: Expecting a numeric value", /* printf() style format */ i ); /* Ignore */ continue; } /* Get a 64-bit integer representation and increment the counter */ n = ph7_value_to_int64(pVal); iTotal += n; } /* Return the count */ ph7_result_int64(pCtx, iTotal); /* All done */ return PH7_OK; } /* * array array_time_func(void) * Return the current system time in an array. * Description * This function does not expects arguments and return the * current system time in an array. * Parameter * None * Return value * Array holding the current system time. * Usage example * * When running you should see something like that: * array(3) { * [0] => * int(14) * [1] => * int(53) * [2] => * int(30) * } */ int array_time_func(ph7_context *pCtx, int argc, ph7_value **argv) { ph7_value *pArray; /* Our Array */ ph7_value *pValue; /* Array entries value */ time_t tt; struct tm *pNow; /* Get the current time first */ time(&tt); pNow = localtime(&tt); /* Create a new array */ pArray = ph7_context_new_array(pCtx); /* Create a worker scalar value */ pValue = ph7_context_new_scalar(pCtx); if( pArray == 0 || pValue == 0 ){ /* * If the supplied memory subsystem is so sick that we are unable * to allocate a tiny chunk of memory, there is no much we can do here. * Abort immediately. */ ph7_context_throw_error(pCtx, PH7_CTX_ERR, "Fatal, PH7 is running out of memory"); /* emulate the die() construct */ return PH7_ABORT; /* die('Fatal, PH7 is running out of memory'); */ } /* Populate the array. * Note that we will use the same worker scalar value (pValue) here rather than * allocating a new value for each array entry. This is due to the fact * that the populated array will make it's own private copy of the inserted * key(if available) and it's associated value. */ ph7_value_int(pValue, pNow->tm_hour); /* Hour */ /* Insert the hour at the first available index */ ph7_array_add_elem(pArray, 0/* NULL: Assign an automatic index*/, pValue /* Will make it's own copy */); /* Overwrite the previous value */ ph7_value_int(pValue, pNow->tm_min); /* Minutes */ /* Insert minutes */ ph7_array_add_elem(pArray, 0/* NULL: Assign an automatic index*/, pValue /* Will make it's own copy */); /* Overwrite the previous value */ ph7_value_int(pValue, pNow->tm_sec); /* Seconds */ /* Insert seconds */ ph7_array_add_elem(pArray, 0/* NULL: Assign an automatic index*/, pValue /* Will make it's own copy */); /* Return the array as the function return value */ ph7_result_value(pCtx, pArray); /* All done. Don't worry about freeing memory here, every * allocated resource will be released automatically by the engine * as soon we return from this foreign function. */ return PH7_OK; } /* * array array_date_func(void) * Return a copy of the 'struct tm' structure in an associative array. * Description * This function does not expects arguments and return a copy of * the 'struct tm' structure found in the 'time.h' header file. * This structure hold the current system date&time. * Parameter * None * Return value * Associative array holding a copy of the 'struct tm' structure. * Usage example * * When running you should see something like that: * array(6) { * [tm_year] => * int(2012) * [tm_mon] => * int(9) * [tm_mday] => * int(23) * [tm_hour] => * int(15) * [tm_min] => * int(53) * [tm_sec] => * int(30) * } */ int array_date_func(ph7_context *pCtx, int argc, ph7_value **argv) { ph7_value *pArray; /* Our Array */ ph7_value *pValue; /* Array entries value */ time_t tt; struct tm *pNow; /* Get the current time first */ time(&tt); pNow = localtime(&tt); /* Create a new array */ pArray = ph7_context_new_array(pCtx); /* Create a worker scalar value */ pValue = ph7_context_new_scalar(pCtx); if( pArray == 0 || pValue == 0 ){ /* * If the supplied memory subsystem is so sick that we are unable * to allocate a tiny chunk of memory, there is no much we can do here. * Abort immediately. */ ph7_context_throw_error(pCtx, PH7_CTX_ERR, "Fatal, PH7 is running out of memory"); /* emulate the die() construct */ return PH7_ABORT; /* die('Fatal, PH7 is running out of memory'); */ } /* Populate the array. * Note that we will use the same worker scalar value (pValue) here rather than * allocating a new value for each array entry. This is due to the fact * that the populated array will make it's own private copy of the inserted * key(if available) and it's associated value. */ ph7_value_int(pValue, pNow->tm_year + 1900); /* Year */ /* Insert Year */ ph7_array_add_strkey_elem(pArray, "tm_year", pValue /* Will make it's own copy */); /* Overwrite the previous value */ ph7_value_int(pValue, pNow->tm_mon + 1); /* Month [1-12]*/ /* Insert month number */ ph7_array_add_strkey_elem(pArray, "tm_mon", pValue /* Will make it's own copy */); /* Overwrite the previous value */ ph7_value_int(pValue, pNow->tm_mday); /* Day of the month [1-31] */ /* Insert the day of the month */ ph7_array_add_strkey_elem(pArray, "tm_mday", pValue /* Will make it's own copy */); ph7_value_int(pValue, pNow->tm_hour); /* Hour */ /* Insert the hour */ ph7_array_add_strkey_elem(pArray, "tm_hour", pValue /* Will make it's own copy */); /* Overwrite the previous value */ ph7_value_int(pValue, pNow->tm_min); /* Minutes */ /* Insert minutes */ ph7_array_add_strkey_elem(pArray, "tm_min", pValue /* Will make it's own copy */); /* Overwrite the previous value */ ph7_value_int(pValue, pNow->tm_sec); /* Seconds */ /* Insert seconds */ ph7_array_add_strkey_elem(pArray, "tm_sec", pValue /* Will make it's own copy */); /* Return the array as the function return value */ ph7_result_value(pCtx, pArray); /* All done. Don't worry about freeing memory here, every * allocated resource will be released automatically by the engine * as soon we return from this foreign function. */ return PH7_OK; } /* * array array_string_split(string $str) * Return a copy of each string character in an array. * Description * This function splits a given string to its * characters and return the result in an array. * Parameter * $str * Target string to split. * Return value * Array holding string characters. * Usage example * * When running you should see something like that: * array(5) { * [0] => * string(1 'H') * [1] => * string(1 'e') * [2] => * string(1 'l') * [3] => * string(1 'l') * [4] => * string(1 'o') * } */ int array_string_split_func(ph7_context *pCtx, int argc, ph7_value **argv) { ph7_value *pArray; /* Our Array */ ph7_value *pValue; /* Array entries value */ const char *zString, *zEnd; /* String to split */ int nByte; /* String length */ /* Make sure there is at least one argument and is of the * expected type [i.e: string]. */ if( argc < 1 || !ph7_value_is_string(argv[0]) ){ /* * Missing/Invalid argument, throw a warning and return FALSE. * Note that you do not need to log the function name, PH7 will * automatically append the function name for you. */ ph7_context_throw_error(pCtx, PH7_CTX_WARNING, "Missing string to split"); /* Return false */ ph7_result_bool(pCtx, 0); return PH7_OK; } /* Extract the target string. * Note that zString is null terminated and ph7_value_to_string() never * fail and always return a pointer to a null terminated string. */ zString = ph7_value_to_string(argv[0], &nByte /* String length */); if( nByte < 1 /* Empty string [i.e: '' or ""] */ ){ ph7_context_throw_error(pCtx, PH7_CTX_NOTICE, "Empty string"); /* Return false */ ph7_result_bool(pCtx, 0); return PH7_OK; } /* Create our array */ pArray = ph7_context_new_array(pCtx); /* Create a scalar worker value */ pValue = ph7_context_new_scalar(pCtx); /* Split the target string */ zEnd = &zString[nByte]; /* Delimit the string */ while( zString < zEnd ){ int c = zString[0]; /* Prepare the character for insertion */ ph7_value_string(pValue, (const char *)&c, (int)sizeof(char)); /* Insert the character */ ph7_array_add_elem(pArray, 0/* NULL: Assign an automatic index */, pValue /* Will make it's own copy*/); /* Erase the previous data from the worker variable */ ph7_value_reset_string_cursor(pValue); /* Next character */ zString++; } /* Return our array as the function return value */ ph7_result_value(pCtx, pArray); /* All done. Don't worry about freeing memory here, every * allocated resource will be released automatically by the engine * as soon we return from this foreign function. */ return PH7_OK; } /* * Container for the foreign functions defined above. * These functions will be registered later using a call * to [ph7_create_function()]. */ static const struct foreign_func { const char *zName; /* Name of the foreign function*/ int (*xProc)(ph7_context *, int, ph7_value **); /* Pointer to the C function performing the computation*/ }aFunc[] = { {"shift_func", shift_func}, {"date_func", date_func}, {"sum_func", sum_func }, {"array_time_func", array_time_func}, {"array_str_split", array_string_split_func}, {"array_date_func", array_date_func} }; /* * Test our implementation: * * The following is the PHP program to execute. * * When running, you should see something like that: * shift_func(150) = 300 * sum_func(7, 8, 9, 10) = 34 * date_func(5) = 2012-23-09 03:53:30 * array_time_func() = * array(3) { * [0] => * int(3) * [1] => * int(53) * [2] => * int(30) * } * array_date_func(5) = * array(6) { * [tm_year] => * int(2012) * [tm_mon] => * int(9) * [tm_mday] => * int(23) * [tm_hour] => * int(3) * [tm_min] => * int(53) * [tm_sec] => * int(30) * } * array_str_split('Hello') = * array(5) { * [0] => * string(1 'H') * [1] => * string(1 'e') * [2] => * string(1 'l') * [3] => * string(1 'l') * [4] => * string(1 'o') * } */ #define PHP_PROG "" #include #include /* * Display an error message and exit. */ static void Fatal(const char *zMsg) { puts(zMsg); /* Shutdown the library */ ph7_lib_shutdown(); /* Exit immediately */ exit(0); } #ifdef __WINNT__ #include #else /* Assume UNIX */ #include #endif /* * The following define is used by the UNIX built and have * no particular meaning on windows. */ #ifndef STDOUT_FILENO #define STDOUT_FILENO 1 #endif /* * VM output consumer callback. * Each time the virtual machine generates some outputs, the following * function gets called by the underlying virtual machine to consume * the generated output. * All this function does is redirecting the VM output to STDOUT. * This function is registered later via a call to ph7_vm_config() * with a configuration verb set to: PH7_VM_CONFIG_OUTPUT. */ static int Output_Consumer(const void *pOutput, unsigned int nOutputLen, void *pUserData/* Unused */) { #ifdef __WINNT__ BOOL rc; rc = WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), pOutput, (DWORD)nOutputLen, 0, 0); if( !rc ){ /* Abort processing */ return PH7_ABORT; } #else ssize_t nWr; nWr = write(STDOUT_FILENO, pOutput, nOutputLen); if( nWr < 0 ){ /* Abort processing */ return PH7_ABORT; } #endif /* __WINT__ */ /* All done, VM output was redirected to STDOUT */ return PH7_OK; } /* * Main program: Register the foreign functions defined above, compile and execute * our PHP test program. */ int main(void) { ph7 *pEngine; /* PH7 engine */ ph7_vm *pVm; /* Compiled PHP program */ int rc; int i; /* Allocate a new PH7 engine instance */ rc = ph7_init(&pEngine); if( rc != PH7_OK ){ /* * If the supplied memory subsystem is so sick that we are unable * to allocate a tiny chunk of memory, there is no much we can do here. */ Fatal("Error while allocating a new PH7 engine instance"); } /* Compile the PHP test program defined above */ rc = ph7_compile_v2( pEngine, /* PH7 engine */ PHP_PROG, /* PHP test program */ -1 /* Compute input length automatically*/, &pVm, /* OUT: Compiled PHP program */ 0 /* IN: Compile flags */ ); if( rc != PH7_OK ){ if( rc == PH7_COMPILE_ERR ){ const char *zErrLog; int nLen; /* Extract error log */ ph7_config(pEngine, PH7_CONFIG_ERR_LOG, &zErrLog, &nLen ); if( nLen > 0 ){ /* zErrLog is null terminated */ puts(zErrLog); } } /* Exit */ Fatal("Compile error"); } /* Now we have our program compiled, it's time to register * our foreign functions. */ for( i = 0 ; i < (int)sizeof(aFunc)/sizeof(aFunc[0]) ; ++i ){ /* Install the foreign function */ rc = ph7_create_function(pVm, aFunc[i].zName, aFunc[i].xProc, 0 /* NULL: No private data */); if( rc != PH7_OK ){ Fatal("Error while registering foreign functions"); } } /* * Configure our VM: * Install the VM output consumer callback defined above. */ rc = ph7_vm_config(pVm, PH7_VM_CONFIG_OUTPUT, Output_Consumer, /* Output Consumer callback */ 0 /* Callback private data */ ); if( rc != PH7_OK ){ Fatal("Error while installing the VM output consumer callback"); } /* * Report run-time errors such as unexpected numbers of arguments and so on. */ ph7_vm_config(pVm, PH7_VM_CONFIG_ERR_REPORT); /* * And finally, execute our program. Note that your output (STDOUT in our case) * should display the result. */ ph7_vm_exec(pVm, 0); /* All done, cleanup the mess left behind. */ ph7_vm_release(pVm); ph7_release(pEngine); return 0; }