/
pyphp-core.c
318 lines (297 loc) · 9.76 KB
/
pyphp-core.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
/**
* pyphp-core.h provides the core functions used by the PyPHP module.
*
* @author Caleb P Burns <cpburns2009@gmail.com>
* @author Ben DeMott <ben_demott@hotmail.com>
* @date 2010-09-30
* @version 0.4
*/
#include <stdio.h>
#include <stdarg.h>
#include <Python.h>
#include <sapi/embed/php_embed.h>
#include "pyphp-core.h"
extern PyObject * pyphp_exception;
/*******************************************************************************
* Converts a Python object (PyObject) to a PHP value (zval).
*
* @param PyObject* pyObj The python object to convert.
* @param zval** phpObj A reference to a zval where the converted zval will be
* stored.
* @return bool On success, true; otherwise, false.
******************************************************************************/
bool pyphp_core_convert_pyObjectToZval(PyObject * pyObj, zval ** phpObj) {
// Make sure pyObj isn't NULL.
if (pyObj == NULL) {
printf("%s:%u PyObject is NULL!\n", __FUNCTION__, __LINE__);
return false;
}
if (phpObj == NULL) {
printf("%s:%u zval reference is NULL!\n", __FUNCTION__, __LINE__);
return false;
}
// Check for Python null value.
if (pyObj == Py_None) {
MAKE_STD_ZVAL(*phpObj);
zval * phpPtr = *phpObj;
ZVAL_NULL(phpPtr);
return true;
}
// Check for Python boolean value.
else if (PyBool_Check(pyObj)) {
MAKE_STD_ZVAL(*phpObj);
zval * phpPtr = *phpObj;
ZVAL_BOOL(phpPtr, PyInt_AsLong(pyObj));
return true;
}
// Check for Python integer value.
else if (PyInt_Check(pyObj) || PyLong_Check(pyObj)) {
MAKE_STD_ZVAL(*phpObj);
zval * phpPtr = *phpObj;
ZVAL_LONG(phpPtr, PyInt_AsLong(pyObj));
return true;
}
// Check for Python floating-point value.
else if (PyFloat_Check(pyObj)) {
MAKE_STD_ZVAL(*phpObj);
zval * phpPtr = *phpObj;
ZVAL_DOUBLE(phpPtr, PyFloat_AsDouble(pyObj));
return true;
}
// Check for Python string.
else if (PyString_Check(pyObj)) {
MAKE_STD_ZVAL(*phpObj);
zval * phpPtr = *phpObj;
char * string = PyString_AsString(pyObj);
ZVAL_STRING(phpPtr, string, 1);
string = NULL;
return true;
}
// Check for a Python unicode string.
// TODO: allow multibyte strings for PHP.
else if (PyUnicode_Check(pyObj)) {
MAKE_STD_ZVAL(*phpObj);
zval * phpPtr = *phpObj;
PyObject * pyAscii = PyUnicode_AsASCIIString(pyObj);
char * ascii = PyString_AsString(pyAscii);
ZVAL_STRING(phpPtr, ascii, 1);
Py_DECREF(pyAscii);
ascii = NULL;
pyAscii = NULL;
return true;
}
// Check for Python dict.
else if (PyDict_Check(pyObj)) {
// Initialize the PHP object to an array.
MAKE_STD_ZVAL(*phpObj);
zval * phpPtr = *phpObj;
array_init(phpPtr);
// Iterate over the python dict, convert the python keys and values into PHP
// keys and values, and append the key-value pairs to the PHP object.
PyObject * pyKey;
PyObject * pyValue;
Py_ssize_t pos = 0;
zval * phpValue;
char * key;
while (PyDict_Next(pyObj, &pos, &pyKey, &pyValue)) {
// Convert the PyObject value into the PHP value.
if (pyphp_core_convert_pyObjectToZval(pyValue, &phpValue)) {
// Convert the PyObject key into the PHP string.
// Append the PHP key-value pair to the PHP object.
key = PyString_AsString(pyKey);
if (zend_hash_update(Z_ARRVAL_P(phpPtr), key, strlen(key), (void *)&phpValue, sizeof(phpValue), NULL) == FAILURE) {
printf("%s:%u Failed to insert zval into hash\n", __FUNCTION__, __LINE__);
zval_dtor(phpValue);
phpValue = NULL;
}
} else {
printf("%s:%u Failed to convert python value to php value\n", __FUNCTION__, __LINE__);
if (phpValue != NULL) {
zval_dtor(phpValue);
phpValue = NULL;
}
return false;
}
}
key = NULL;
phpValue = NULL;
pyValue = NULL;
pyKey = NULL;
return true;
}
// Check for Python list/tuple.
else if (PySequence_Check(pyObj)) {
// Initialize the PHP object to an array.
MAKE_STD_ZVAL(*phpObj);
zval * phpPtr = *phpObj;
array_init(phpPtr);
// Iterate over the python sequence items, convert the python items into
// PHP values, and append the items to the PHP object.
const Py_ssize_t seqSize = PySequence_Size(pyObj);
PyObject * pyItem;
zval * phpItem;
Py_ssize_t i;
for (i = 0; i < seqSize; i++) {
// Get the python sequence item at index.
pyItem = PySequence_GetItem(pyObj, i);
if (pyItem != NULL) {
// Convert the PyObject item into the PHP item.
phpItem = NULL;
if (pyphp_core_convert_pyObjectToZval(pyItem, &phpItem)) {
// Append the PHP item to the PHP object.
//add_next_index_zval(phpPtr, phpItem);
if (zend_hash_next_index_insert(Z_ARRVAL_P(phpPtr), &phpItem, sizeof(phpItem), NULL) == FAILURE) {
printf("%s:%u Failed to insert zval into hash\n", __FUNCTION__, __LINE__);
zval_dtor(phpItem);
return false;
}
} else {
printf("%s:%u Failed to convert python value to php value\n", __FUNCTION__, __LINE__);
if (phpItem != NULL) {
zval_dtor(phpItem);
phpItem = NULL;
}
return false;
}
// Delete the reference to the python item.
Py_DECREF(pyItem);
}
}
phpItem = NULL;
pyItem = NULL;
return true;
}
printf("%s:%u The PyObject's type is not supported by this function!", __FUNCTION__, __LINE__);
return false;
}
/*******************************************************************************
* The PHP error handler.
*
* @param int type The error type.
* @param char* file The PHP file the error occured in.
* @param uint line The line the error occured on.
* @param char* format The format?
* @param va-list args The Variadic arguments for the format.
******************************************************************************/
void pyphp_core_php_errorHandler(int type, const char * file, const unsigned int line, const char * format, va_list args) {
char * error;
bool isFatal = false;
switch (type) {
case E_ERROR:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
isFatal = true;
error = "Fatal error";
break;
case E_RECOVERABLE_ERROR:
error = "Catchable fatal error";
break;
case E_WARNING:
case E_CORE_WARNING:
case E_COMPILE_WARNING:
case E_USER_WARNING:
error = "Warning";
break;
case E_PARSE:
error = "Parse error";
break;
case E_NOTICE:
case E_USER_NOTICE:
error = "Notice";
break;
case E_STRICT:
error = "Strict Standards";
break;
case E_DEPRECATED:
case E_USER_DEPRECATED:
error = "Deprecated";
break;
default:
error = "Unknown error";
break;
}
// If the error is fatal, raise a python exception.
if (isFatal) {
// Copy args so that the args don't become corrupted when passed to the
// internal PHP error handler.
va_list vars;
va_copy(vars, args);
char * message;
vspprintf(&message, PG(log_errors_max_len), format, vars);
PyErr_Format(pyphp_exception, "PHP %s: %s in %s on line %u", error, message, file, line);
// Clean up.
efree(message);
message = NULL;
}
// Call the internal PHP error handler.
pyphp_core.phpInternalErrorHandler(type, file, line, format, args);
}
/*******************************************************************************
* The PHP log handler.
*
* @param char* messsage The log message.
******************************************************************************/
void pyphp_core_php_logHandler(char * message) {
// Call the PHP log handler python callback function if it's set.
if (pyphp_core.pyLogHandler) {
PyObject * pyArgs = Py_BuildValue("{s:s}:pyphp.pyphp_core_php_logHandler", "message", message);
PyObject * pyResult = PyEval_CallObject(pyphp_core.pyOutputHandler, pyArgs);
Py_XDECREF(pyResult);
Py_DECREF(pyArgs);
pyResult = NULL;
pyArgs = NULL;
return;
}
// Since no log handler was specified, send log message to the log stream
// (usually stdout or stdlog).
//fprintf(pyphp_core.logStream, "%s\n", message);
fflush(pyphp_core.logStream);
}
/*******************************************************************************
* The PHP output handler.
*
* @param char* messsage The output message.
* @return int The number of characters written.
******************************************************************************/
int pyphp_core_php_outputHandler(const char * message, unsigned int length TSRMLS_DC) {
// Call the PHP output handler python callback function if it's set.
if (pyphp_core.pyOutputHandler) {
PyObject * pyArgs = Py_BuildValue("{s:s}:pyphp.pyphp_core_php_outputHandler", "message", message);
PyObject * pyResult = PyEval_CallObject(pyphp_core.pyOutputHandler, pyArgs);
Py_XDECREF(pyResult);
Py_DECREF(pyArgs);
pyResult = NULL;
pyArgs = NULL;
return length;
}
// Since no output handler was specified, send output message to the output
// stream (usually stdout).
fwrite(message, sizeof(char), length, pyphp_core.outputStream);
fflush(pyphp_core.outputStream);
return length;
}
/*******************************************************************************
* The PHP output flush handler.
******************************************************************************/
void pyphp_core_php_outputFlushHandler(void * server_context) {
fflush(pyphp_core.outputStream);
}
/*******************************************************************************
* The PHP startup handler.
*
* @hack Stores and overrides the internal PHP error handler.
*
* @param sapi_module_struct* sapi_module The SAPI module to startup.
* @return int On success, SUCCESS (0); otherwise, FAILURE (1).
******************************************************************************/
int pyphp_core_php_startup(sapi_module_struct * sapi_module) {
if (php_module_startup(sapi_module, NULL, 0) == FAILURE) {
return FAILURE;
}
//HACK: Grab the function pointer to the internal PHP (zend) error handler.
pyphp_core.phpInternalErrorHandler = zend_error_cb;
//HACK: Override the internal PHP error handler.
zend_error_cb = pyphp_core_php_errorHandler;
return SUCCESS;
}