Skip to content

Commit

Permalink
Import array/string object
Browse files Browse the repository at this point in the history
  • Loading branch information
matyhtf committed Dec 25, 2023
1 parent 99fb7eb commit 66f0521
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 63 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ project(phpy)

#set(CMAKE_BUILD_TYPE Released)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -g -z now")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -g")

if (NOT DEFINED PHP_CONFIG)
set(PHP_CONFIG "php-config")
Expand Down
2 changes: 2 additions & 0 deletions include/phpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,12 +304,14 @@ namespace python {
PyObject *new_array(zval *zv);
PyObject *new_array(PyObject *pv);
PyObject *new_string(zval *zv);
PyObject *new_string(size_t len);
PyObject *new_string(PyObject *pv);
PyObject *new_object(zval *zv);
PyObject *new_resource(zval *zv);
PyObject *new_reference(zval *zv);
PyObject *new_callable(zval *zv);
const char *string2utf8(PyObject *pv, ssize_t *len);
const char *string2char_ptr(PyObject *pv, ssize_t *len);
void tuple2argv(zval *argv, PyObject *args, ssize_t size, int begin = 1);
void release_argv(uint32_t argc, zval *argv);
} // namespace python
Expand Down
19 changes: 19 additions & 0 deletions src/bridge/core.cc
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,25 @@ namespace python {
const char *string2utf8(PyObject *pv, ssize_t *len) {
return PyUnicode_AsUTF8AndSize(pv, len);
};
const char *string2char_ptr(PyObject *pv, ssize_t *len) {
const char *c_str;
if (ZendString_Check(pv)) {
zval *z2 = zend_string_cast(pv);
*len = Z_STRLEN_P(z2);
c_str = Z_STRVAL_P(z2);
} else if (PyByteArray_Check(pv)) {
c_str = PyByteArray_AS_STRING(pv);
*len = PyByteArray_GET_SIZE(pv);
} else if (PyBytes_Check(pv)) {
c_str = PyBytes_AS_STRING(pv);
*len = PyBytes_GET_SIZE(pv);
} else if (PyUnicode_Check(pv)) {
c_str = PyUnicode_AsUTF8AndSize(pv, len);
} else {
return NULL;
}
return c_str;
}
void tuple2argv(zval *argv, PyObject *args, ssize_t size, int begin) {
Py_ssize_t i;
for (i = begin; i < size; i++) {
Expand Down
82 changes: 55 additions & 27 deletions src/python/array.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ struct ZendArray {
zval array;
};

static PyMappingMethods Array_mp_methods = {};

static PyMethodDef Array_methods[] = {
{"get", (PyCFunction) Array_get, METH_VARARGS, "Get array item value" },
{"set", (PyCFunction) Array_set, METH_VARARGS, "Set array item value" },
Expand Down Expand Up @@ -63,12 +65,8 @@ static int Array_init(ZendArray *self, PyObject *args, PyObject *kwds) {
return 0;
}

static PyObject *Array_get(ZendArray *self, PyObject *args) {
static PyObject *Array_getitem(ZendArray *self, PyObject *key) {
zval *result;
PyObject *key;
if (!PyArg_ParseTuple(args, "O", &key)) {
return NULL;
}
if (PyLong_Check(key)) {
result = phpy::php::array_get(&self->array, PyLong_AsLong(key));
} else {
Expand All @@ -77,68 +75,98 @@ static PyObject *Array_get(ZendArray *self, PyObject *args) {
result = phpy::php::array_get(&self->array, skey, l_key);
}
if (!result) {
Py_INCREF(Py_None);
return Py_None;
Py_RETURN_NONE;
}
return php2py_object(result);
}

static PyObject *Array_set(ZendArray *self, PyObject *args) {
PyObject *value;
static PyObject *Array_get(ZendArray *self, PyObject *args) {
PyObject *key;
if (!PyArg_ParseTuple(args, "OO", &key, &value)) {
if (!PyArg_ParseTuple(args, "O", &key)) {
return NULL;
}
return Array_getitem(self, key);
}

static bool Array_delitem(ZendArray *self, PyObject *key) {
zend_result result;
if (PyLong_Check(key)) {
result = zend_hash_index_del(Z_ARR(self->array), PyLong_AsLong(key));
} else {
ssize_t l_key;
auto skey = phpy::python::string2utf8(key, &l_key);
result = zend_hash_str_del(Z_ARR(self->array), skey, l_key);
}
return result == SUCCESS;
}

static int Array_setitem(ZendArray *self, PyObject *key, PyObject *value) {
// value be set to NULL to delete an item
if (value == NULL) {
return Array_delitem(self, key) ? 0 : -1;
}
zval rv;
py2php(value, &rv);
zval *result;
if (PyLong_Check(key)) {
zend_hash_index_update(Z_ARR(self->array), PyLong_AsLong(key), &rv);
result = zend_hash_index_update(Z_ARR(self->array), PyLong_AsLong(key), &rv);
} else {
ssize_t l_key;
auto skey = phpy::python::string2utf8(key, &l_key);
zend_hash_str_update(Z_ARR(self->array), skey, l_key, &rv);
result = zend_hash_str_update(Z_ARR(self->array), skey, l_key, &rv);
}
Py_INCREF(Py_None);
return Py_None;
return result == NULL ? -1 : 0;
}

static PyObject *Array_unset(ZendArray *self, PyObject *args) {
zend_result result;
static PyObject *Array_set(ZendArray *self, PyObject *args) {
PyObject *value;
PyObject *key;
if (!PyArg_ParseTuple(args, "O", &key)) {
if (!PyArg_ParseTuple(args, "OO", &key, &value)) {
return NULL;
}
if (PyLong_Check(key)) {
result = zend_hash_index_del(Z_ARR(self->array), PyLong_AsLong(key));
if (Array_setitem(self, key, value) == 0) {
Py_RETURN_TRUE;
} else {
ssize_t l_key;
auto skey = phpy::python::string2utf8(key, &l_key);
result = zend_hash_str_del(Z_ARR(self->array), skey, l_key);
Py_RETURN_FALSE;
}
if (result == SUCCESS) {
Py_INCREF(Py_True);
return Py_True;
}

static PyObject *Array_unset(ZendArray *self, PyObject *args) {
PyObject *key;
if (!PyArg_ParseTuple(args, "O", &key)) {
return NULL;
}
if (Array_delitem(self, key)) {
Py_RETURN_TRUE;
} else {
Py_INCREF(Py_False);
return Py_False;
Py_RETURN_FALSE;
}
}

static PyObject *Array_count(ZendArray *self, PyObject *args) {
return PyLong_FromLong(phpy::php::array_count(&self->array));
}

static Py_ssize_t Array_len(ZendArray *self) {
return phpy::php::array_count(&self->array);
}

static void Array_destroy(ZendArray *self) {
zval_ptr_dtor(&self->array);
Py_TYPE(self)->tp_free((PyObject *) self);
phpy::php::del_object((PyObject *) self);
}

bool py_module_array_init(PyObject *m) {
Array_mp_methods.mp_length = (lenfunc) Array_len;
Array_mp_methods.mp_subscript = (binaryfunc) Array_getitem;
Array_mp_methods.mp_ass_subscript = (objobjargproc) Array_setitem;

ZendArrayType.tp_name = "zend_array";
ZendArrayType.tp_basicsize = sizeof(ZendArray);
ZendArrayType.tp_itemsize = 0;
ZendArrayType.tp_dealloc = (destructor) Array_destroy;
ZendArrayType.tp_as_mapping = &Array_mp_methods;
ZendArrayType.tp_flags = Py_TPFLAGS_DEFAULT;
ZendArrayType.tp_doc = PyDoc_STR("zend_array");
ZendArrayType.tp_methods = Array_methods;
Expand Down
111 changes: 83 additions & 28 deletions src/python/string.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@

struct ZendString;
static int String_init(ZendString *self, PyObject *args, PyObject *kwds);
static PyObject *String_len(ZendString *self, PyObject *args);
static PyObject *String_bytes(ZendString *self, PyObject *args);
static PyObject *String_compare(PyObject *o1, PyObject *o2, int op);
static PyObject *String_str(ZendString *self);
static void String_destroy(ZendString *self);

// clang-format off
Expand All @@ -31,8 +28,9 @@ struct ZendString {
zval string;
};

static PySequenceMethods String_sq_methods = {};

static PyMethodDef String_methods[] = {
{"len", (PyCFunction) String_len, METH_NOARGS, "Get string length" },
{"__bytes__", (PyCFunction) String_bytes, METH_NOARGS, "Convert to bytes" },
{NULL} /* Sentinel */
};
Expand All @@ -48,13 +46,17 @@ static void String_dtor(PyObject *pv) {
}

static int String_init(ZendString *self, PyObject *args, PyObject *kwds) {
const char *str;
size_t len;
if (!PyArg_ParseTuple(args, "s#", &str, &len)) {
const char *str = NULL;
size_t len = 0;
if (!PyArg_ParseTuple(args, "|s#", &str, &len)) {
PyErr_SetString(PyExc_TypeError, "must supply at least 1 parameter.");
return -1;
}
ZVAL_STRINGL(&self->string, str, len);
if (str == NULL) {
ZVAL_EMPTY_STRING(&self->string);
} else {
ZVAL_STRINGL(&self->string, str, len);
}
phpy::php::add_object((PyObject *) self, String_dtor);
return 0;
}
Expand All @@ -67,39 +69,80 @@ static PyObject *String_bytes(ZendString *self, PyObject *args) {
return PyBytes_FromStringAndSize(Z_STRVAL_P(&self->string), Z_STRLEN_P(&self->string));
}

static PyObject *String_iadd(ZendString *self, PyObject *o2) {
size_t s1_len = Z_STRLEN(self->string);
ssize_t s2_len;
const char *s2 = phpy::python::string2char_ptr(o2, &s2_len);
if (s2 == NULL) {
PyErr_Format(PyExc_TypeError, "can not concat '%s' to zend_string", Py_TYPE(o2)->tp_name);
return NULL;
}
zend_string *new_zstr = zend_string_extend(Z_STR(self->string), s1_len + s2_len, 0);
if (!new_zstr) {
PyErr_SetString(PyExc_MemoryError, "memory alloc fail");
return NULL;
}
Z_STR(self->string) = new_zstr;
memcpy(Z_STRVAL(self->string) + s1_len, s2, s2_len);
Py_INCREF(self);
return (PyObject *) self;
}

static PyObject *String_add(ZendString *self, PyObject *o2) {
size_t s1_len = Z_STRLEN(self->string);
const char *s1 = Z_STRVAL(self->string);
ssize_t s2_len;
const char *s2 = phpy::python::string2char_ptr(o2, &s2_len);
if (s2 == NULL) {
PyErr_Format(PyExc_TypeError, "can not concat '%s' to zend_string", Py_TYPE(o2)->tp_name);
return NULL;
}
ZendString *new_str = (ZendString *) phpy::python::new_string(s1_len + (size_t) s2_len);
memcpy(Z_STRVAL(new_str->string), s1, s1_len);
memcpy(Z_STRVAL(new_str->string) + s1_len, s2, s2_len);
return (PyObject *) new_str;
}

static PyObject *String_compare(PyObject *o1, PyObject *o2, int op) {
if (op != Py_EQ) {
Py_RETURN_NOTIMPLEMENTED;
}
bool equals;
zval *z1 = zend_string_cast(o1);
if (ZendString_Check(o2)) {
zval *z2 = zend_string_cast(o2);
equals = zend_string_equal_content(Z_STR_P(z1), Z_STR_P(z2));
} else if (PyByteArray_Check(o2)) {
const char *val = PyByteArray_AS_STRING(o2);
size_t len = PyByteArray_GET_SIZE(o2);
equals = (len == Z_STRLEN_P(z1) && memcmp(Z_STRVAL_P(z1), val, len) == 0);
} else if (PyBytes_Check(o2)) {
const char *val = PyBytes_AS_STRING(o2);
size_t len = PyBytes_GET_SIZE(o2);
equals = (len == Z_STRLEN_P(z1) && memcmp(Z_STRVAL_P(z1), val, len) == 0);
} else if (PyUnicode_Check(o2)) {
Py_ssize_t len;
const char *val = PyUnicode_AsUTF8AndSize(o2, &len);
equals = (len == (Py_ssize_t) Z_STRLEN_P(z1) && memcmp(Z_STRVAL_P(z1), val, len) == 0);
} else {
ssize_t len;
const char *val = phpy::python::string2char_ptr(o2, &len);
if (val == NULL) {
Py_RETURN_NOTIMPLEMENTED;
}
if (equals) {
if (len == (Py_ssize_t) Z_STRLEN_P(z1) && memcmp(Z_STRVAL_P(z1), val, len) == 0) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}

static PyObject *String_len(ZendString *self, PyObject *args) {
return PyLong_FromLong(Z_STRLEN_P(&self->string));
static Py_ssize_t String_len(ZendString *self) {
return Z_STRLEN_P(&self->string);
}

static PyObject *String_at(ZendString *self, Py_ssize_t offset) {
if (offset >= (Py_ssize_t) Z_STRLEN_P(&self->string)) {
PyErr_SetString(PyExc_IndexError, "zend_string index out of range");
return NULL;
}
return PyLong_FromUnsignedLong((unsigned long) Z_STRVAL_P(&self->string)[offset]);
}

static PyObject *String_contains(ZendString *self, PyObject *o2) {
ssize_t len;
const char *val = phpy::python::string2char_ptr(o2, &len);
if (val == NULL) {
Py_RETURN_NOTIMPLEMENTED;
}
if (php_memnstr(Z_STRVAL(self->string), val, len, Z_STRVAL(self->string) + Z_STRLEN(self->string))) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}

static void String_destroy(ZendString *self) {
Expand All @@ -109,10 +152,17 @@ static void String_destroy(ZendString *self) {
}

bool py_module_string_init(PyObject *m) {
String_sq_methods.sq_length = (lenfunc) String_len;
String_sq_methods.sq_item = (ssizeargfunc) String_at;
String_sq_methods.sq_concat = (binaryfunc) String_add;
String_sq_methods.sq_contains = (objobjproc) String_contains;
String_sq_methods.sq_inplace_concat = (binaryfunc) String_iadd;

ZendStringType.tp_name = "zend_string";
ZendStringType.tp_basicsize = sizeof(ZendString);
ZendStringType.tp_itemsize = 0;
ZendStringType.tp_dealloc = (destructor) String_destroy;
ZendStringType.tp_as_sequence = &String_sq_methods;
ZendStringType.tp_str = (reprfunc) String_str;
ZendStringType.tp_flags = Py_TPFLAGS_DEFAULT;
ZendStringType.tp_doc = PyDoc_STR("zend_string");
Expand Down Expand Up @@ -173,5 +223,10 @@ PyObject *new_string(zval *zv) {
zval_add_ref(&self->string);
return (PyObject *) self;
}
PyObject *new_string(size_t len) {
zval nv;
Z_STR(nv) = zend_string_alloc(len, 0);
return new_string(&nv);
}
} // namespace python
} // namespace phpy
19 changes: 18 additions & 1 deletion tests/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,22 @@ def test_assoc_array():
assert d.get("uuid") == str(uuid)
assert d.get("php") == "swoole"
assert d.unset("php")
assert d.unset("php") == False
assert d.unset("php") is False
assert d.get("php") is None


def test_mp_protocol():
uuid = phpy.call("uniqid")
d = phpy.Array({"hello": "world", "php": "swoole", "uuid": uuid})

uuid2 = phpy.call("uniqid")

assert d['test'] is None

assert len(d) == 3
assert d['uuid'] == uuid
d['test'] = uuid2
assert d['test'] == uuid2

del d['test']
assert d['test'] is None
Loading

0 comments on commit 66f0521

Please sign in to comment.