How can I store the output of php_execute_script() in a character array? php_execute_script() only prints the output.
Here's what I've got:
PHP_EMBED_START_BLOCK(argc, argv)
char string[200];
setbuf(stdout, string);
zend_file_handle file_handle;
zend_stream_init_filename(&file_handle, "say.php");
fflush(stdout);
if (php_execute_script(&file_handle) == FAILURE) {
php_printf("Failed to execute PHP script.\n");
}
setbuf(stdout, NULL);
printf("\"%s\" is what say.php has to say.\n", string);
PHP_EMBED_END_BLOCK()
I've tried redirecting stdout to string, but it doesn't even look like php_execute_script is actually writing to stdout! It just ignores it.
I'm trying to communicate with the PHP embedded script ("say.php") from C using the PHP SAPI after building PHP with embedding enabled.
Thinking that there's no such thing, I made an issue:
https://github.com/php/php-src/issues/9330
#include <sapi/embed/php_embed.h>
char *string[999] = {NULL};
int i = 0;
static size_t embed_ub_write(const char *str, size_t str_length){
string[i] = (char *) str; i++;
return str_length;
}
int main(int argc, char **argv)
{
php_embed_module.ub_write = embed_ub_write;
PHP_EMBED_START_BLOCK(argc, argv)
zend_file_handle file_handle;
zend_stream_init_filename(&file_handle, "say.php");
if (php_execute_script(&file_handle) == FAILURE) {
php_printf("Failed to execute PHP script.\n");
}
for (i = 0; string[i] != NULL; i++) {
printf("%s", string[i]);
}
PHP_EMBED_END_BLOCK()
}
You need to set php_embed_module.ub_write to a callback function that gets called every time PHP makes an echo.
It's weird how PHP doesn't write to stdout...
EDIT: As it turns out, it does. You just need to use pipe.
Related
I got a bit of a stupid question;
Currently I am making a website for a company on a server which actually has a bit an outdated PHP version (5.2.17). I have a database in which many fields are varchar with characters like 'é ä è ê' and so on, which I have to display in an HTML page.
So as the version of PHP is outdated (and I am not allowed to updated it because there are parts of the site that must keep working and to whom I have no acces to edit them) I can't use the htmlentities function with the ENT_SUBSTITUTE argument, because it was only added after version 5.4.
So my question is:
Does there exist an alternative to
htmlentities($string,ENT_SUBSTITUTE); or do I have to write a function
myself with all kinds of strange characters, which would be incomplete anyway.
Define a function for handling ill-formed byte sequences and call the function before passing the string to htmlentties. There are various way to define the function.
At first, try UConverter::transcode if you don't use Windows.
http://pecl.php.net/package/intl
If you are willing to handle bytes directly, see my previous answer.
https://stackoverflow.com/a/13695364/531320
The last option is to develop PHP extension. Thanks to php_next_utf8_char, it's not hard.
Here is code sample. The name "scrub" comes from Ruby 2.1 (see Equivalent of Iconv.conv("UTF-8//IGNORE",...) in Ruby 1.9.X?)
// header file
// PHP_FUNCTION(utf8_scrub);
#include "ext/standard/html.h"
#include "ext/standard/php_smart_str.h"
const zend_function_entry utf8_string_functions[] = {
PHP_FE(utf8_scrub, NULL)
PHP_FE_END
};
PHP_FUNCTION(utf8_scrub)
{
char *str = NULL;
int len, status;
size_t pos = 0, old_pos;
unsigned int code_point;
smart_str buf = {0};
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len) == FAILURE) {
return;
}
while (pos < len) {
old_pos = pos;
code_point = php_next_utf8_char((const unsigned char *) str, len, &pos, &status);
if (status == FAILURE) {
smart_str_appendl(&buf, "\xEF\xBF\xBD", 3);
} else {
smart_str_appendl(&buf, str + old_pos, pos - old_pos);
}
}
smart_str_0(&buf);
RETURN_STRINGL(buf.c, buf.len, 0);
smart_str_free(&buf);
}
You don't need ENT_SUBSTITUTE if your encoding is handled correctly.
If the characters in your database are utf-8, stored in utf-8, read in utf-8 and displayed to the user in utf-8 there should be no problem.
Just add
if (!defined('ENT_SUBSTITUTE')) define('ENT_SUBSTITUTE', 0);
and you'll be able to use ENT_SUBSTITUTE into htmlentities.
I am replacing zend_compile_string() for code pre-processing, which then pass to the original zend_compile_string().
It works.
But I also need code pre-process obtained from the files (_include/require, php <file.php>).
Zend provides the ability to replace zend_compile_file(), but the source text is available only inside open_file_for_scanning() (zend_stream_fixup(file_handle, &buf, &size TSRMLS_CC)), which is not accessible from the extension.
How can I change the code before being sent to zendparse()?
Edit: I found a solution:
zend_op_array* ext_zend_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC)
{
char *buf;
size_t size;
if (zend_stream_fixup(file_handle, &buf, &size TSRMLS_CC) == FAILURE) {
return NULL;
}
char *res;
size_t res_size;
// My code that uses file_handle->handle.stream.mmap.buf/len is read-only and filling res/res_size
file_handle->handle.stream.mmap.buf = res;
file_handle->handle.stream.mmap.len = res_size;
return ext_orig_zend_compile_file(file_handle, type TSRMLS_CC);
}
I want to ask you how it should look this code to C++:
<?php
for ($i = 1; $i <= 10; $i++) {
$array[$i]="test".$i;
}
?>
It would look something like this (a complete program).
/* required headers */
#include <map>
#include <cstdlib>
/* code has to be inside a function; main is the start-point of the program */
int main() {
std::map<int, int> array;
for (int i = 1; i <= 10; ++i) {
array[i] = i;
}
return EXIT_SUCCESS;
}
I use a map above, since PHP "arrays" are actually like maps in other languages (although completely mimicing their behaviour in a statically-typed language is a hassle). Of course, since the program does little, you could save yourself some typing and not type something that effectively does nothing.
EDIT:
/* required headers */
#include <map>
#include <string>
#include <sstream>
#include <cstdlib>
/* code has to be inside a function; main is the start-point of the program */
int main() {
std::map<int, std::string> array;
for (int i = 1; i <= 10; ++i) {
std::ostringstream stream;
stream << "test" << i;
array[i] = stream.str();
}
return EXIT_SUCCESS;
}
Edit based on your edits
Important addition to the other answers that php doesn't require:
#include <sstream>
#include <string>
int main
{
using namespace std;
string array[11]; // tell the compiler array is an array of size 11
// this array starts at index 0 and goes up to 10
// totaling 11 elements
for ( int i=1; i<=10; i++){ // you might want to start at 0 here
ostringstream strStream
strStream << "test" << i;
array[i] = strStream.str();
}
return 0;
}
// accessing array outside the bounds you told the compiler
// results in undefined behavior, practically this means crash
// or data corruption
This is the most direct conversion. However, you probably wish to look into std::map in #eq-'s answer for general associative containers where you don't care if the index space is contiguous and just general safety. C style arrays are considered a code smell these days.
for ( int i=1; i<=10; i++){
array[i] = i;
}
Okay, I've written my first functional PHP extension. It worked but it was a proof-of-concept only. Now I'm writing another one which actually does what the boss wants.
What I'd like to know, from all you PHP-heads out there, is whether this code makes sense. Have I got a good grasp of things like emalloc and the like, or is there stuff there that's going to turn around later and try to bite my hand off?
Below is the code for one of the functions. It returns a base64 of a string that has also been Blowfish encrypted. When the function is called, it is supplied with two strings, the text to encrypt and encode, and the key for the encryption phase. It's not using PHP's own base64 functions because, at this point, I don't know how to link to them. And it's not using PHP's own mcrypt functions for the same reason. Instead, it links in the SSLeay BF_ecb_encrypt functions.
PHP_FUNCTION(Blowfish_Base64_encode)
{
char *psData = NULL;
char *psKey = NULL;
int argc = ZEND_NUM_ARGS();
int psData_len;
int psKey_len;
char *Buffer = NULL;
char *pBuffer = NULL;
char *Encoded = NULL;
BF_KEY Context;
int i = 0;
unsigned char Block[ 8 ];
unsigned char * pBlock = Block;
char *plaintext;
int plaintext_len;
int cipher_len = 0;
if (zend_parse_parameters(argc TSRMLS_CC, "ss", &psData, &psData_len, &psKey, &psKey_len) == FAILURE)
return;
Buffer = (char *) emalloc( psData_len * 2 );
pBuffer = Buffer;
Encoded = (char *) emalloc( psData_len * 4 );
BF_set_key( &Context, psKey_len, psKey );
plaintext = psData;
plaintext_len = psData_len;
for (;;)
{
if (plaintext_len--)
{
Block[ i++ ] = *plaintext++;
if (i == 8 )
{
BF_ecb_encrypt( Block, pBuffer, &Context, BF_ENCRYPT );
pBuffer += 8;
cipher_len += 8;
memset( Block, 0, 8 );
i = 0;
}
} else {
BF_ecb_encrypt( Block, pBuffer, &Context, BF_ENCRYPT );
cipher_len += 8;
break;
}
}
b64_encode( Encoded, Buffer, cipher_len );
RETURN_STRINGL( Encoded, strlen( Encoded ), 0 );
}
You'll notice that I have two emalloc calls, for Encoded and for Buffer. Only Encoded is passed back to the caller, so I'm concerned that Buffer won't be freed. Is that the case? Should I use malloc/free for Buffer?
If there are any other glaring errors, I'd really appreciate knowing.
emalloc() allocates memory per request, and it's free()'d automatically when the runtime ends.
You should, however, compile PHP with
--enable-debug --enable-maintainer-zts
It will tell you if anything goes wrong (it can detect memory leaks if you've used the e*() functions and report_memleaks is set in your php.ini).
And yes, you should efree() Buffer.
You'll notice that I have two emalloc calls, for Encoded and for Buffer. Only Encoded is passed back to the caller, so I'm concerned that Buffer won't be freed. Is that the case? Should I use malloc/free for Buffer?
Yes, you should free it with efree before returning.
Although PHP has safety net and memory allocated with emalloc will be freed at the end of the request, it's still a bug to leak memory and, depending you will warned if running a debug build with report_memleaks = On.
I'm writing a PHP extension that takes a reference to a value and alters it. Example PHP:
$someVal = "input value";
TestPassRef($someVal);
// value now changed
What's the right approach?
Edit 2011-09-13:
The correct way to do this is to use the ZEND_BEGIN_ARG_INFO() family of macros - see Extending and Embedding PHP chapter 6 (Sara Golemon, Developer's Library).
This example function takes one string argument by value (due to the ZEND_ARG_PASS_INFO(0) call) and all others after that by reference (due to the second argument to ZEND_BEGIN_ARG_INFO being 1).
const int pass_rest_by_reference = 1;
const int pass_arg_by_reference = 0;
ZEND_BEGIN_ARG_INFO(AllButFirstArgByReference, pass_rest_by_reference)
ZEND_ARG_PASS_INFO(pass_arg_by_reference)
ZEND_END_ARG_INFO()
zend_function_entry my_functions[] = {
PHP_FE(TestPassRef, AllButFirstArgByReference)
};
PHP_FUNCTION(TestPassRef)
{
char *someString = NULL;
int lengthString = 0;
zval *pZVal = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &someString, &lengthString, &pZVal) == FAILURE)
{
return;
}
convert_to_null(pZVal); // Destroys the value that was passed in
ZVAL_STRING(pZVal, "some string that will replace the input", 1);
}
Before adding the convert_to_null it would leak memory on every call (I've not whether this is necessary after adding ZENG_ARG_INFO() calls).