http://devzone.zend.com/article/4486教程之后,我尝试将几个C++类包装到PHP扩展中,不幸的是,它失败了。我希望有人可以帮助我。为了尝试简化问题解决方案,我还简化了我的类(class)。

目的是要有允许我执行一些多边形操作的类。然后,我创建了Point类和Polygon类,如下所示:

多边形

#ifndef POLYGON_H
#define POLYGON_H 1

#include <vector>

class Point {
  double __x;
  double __y;
public:
  Point(double x, double y);
  double x(void);
  double y(void);
};

class Polygon {
  std::vector<Point> __pts;
public:
  void add(Point pnt);
  Point& get(unsigned long idx);
  unsigned long size(void);
};

#endif

多边形
#include "polygon.h"

Point::Point(double x, double y) : __x(x), __y(y) {
}

double Point::x(void) {
    return __x;
}

double Point::y(void) {
    return __y;
}

void Polygon::add(Point pnt) {
    __pts.push_back(pnt);
}

Point& Polygon::get(unsigned long idx) {
    return __pts.at(idx);
}

unsigned long Polygon::size(void) {
    return __pts.size();
}

配置文件
PHP_ARG_ENABLE(geometry,
    [Whether to enable the "geometry" extension],
    [  --enable-geometry      Enable "geometry" extension support])

if test $PHP_GEOMETRY != "no"; then
    PHP_REQUIRE_CXX()
    PHP_SUBST(GEOMETRY_SHARED_LIBADD)
    PHP_ADD_LIBRARY(stdc++, 1, GEOMETRY_SHARED_LIBADD)
    PHP_NEW_EXTENSION(geometry, geometry.cpp polygon.cpp, $ext_shared)
fi

php_geometry.h
#ifndef PHP_GEOMETRY_H
#define PHP_GEOMETRY_H 1

#define PHP_GEOMETRY_EXTNAME  "geometry"
#define PHP_GEOMETRY_EXTVER   "0.1"

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

extern "C" {
#include "php.h"
}

extern zend_module_entry geometry_module_entry;
#define phpext_geometry_ptr &geometry_module_entry;

PHP_METHOD(Point, __construct);
PHP_METHOD(Point, x);
PHP_METHOD(Point, y);

PHP_METHOD(Polygon, __construct);
PHP_METHOD(Polygon, add);
PHP_METHOD(Polygon, get);
PHP_METHOD(Polygon, size);

#endif

geometry.cpp
#include "php_geometry.h"

#include "polygon.h"

zend_object_handlers point_object_handlers;
zend_object_handlers polygon_object_handlers;

struct point_object {
  zend_object std;
  Point* point;
};

struct polygon_object {
  zend_object std;
  Polygon* polygon;
};

zend_class_entry* point_ce;
zend_class_entry* polygon_ce;

void point_free_storage(void* object TSRMLS_DC) {
    point_object* obj = (point_object*)object;
    delete obj->point;

    zend_hash_destroy(obj->std.properties);
    FREE_HASHTABLE(obj->std.properties);

    efree(obj);
}

void polygon_free_storage(void* object TSRMLS_DC) {
    polygon_object* obj = (polygon_object*)object;
    delete obj->polygon;

    zend_hash_destroy(obj->std.properties);
    FREE_HASHTABLE(obj->std.properties);

    efree(obj);
}

zend_object_value point_create_handler(zend_class_entry* type TSRMLS_DC) {
    zval* tmp;
    zend_object_value retval;

    point_object* obj = (point_object*)emalloc(sizeof(point_object));
    memset(obj, 0, sizeof(point_object));
    obj->std.ce = type;

    ALLOC_HASHTABLE(obj->std.properties);
    zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
    zend_hash_copy(obj->std.properties, &type->default_properties, (copy_ctor_func_t)zval_add_ref, (void*)&tmp, sizeof(zval*));

    retval.handle = zend_objects_store_put(obj, NULL,
        point_free_storage, NULL TSRMLS_CC);
    retval.handlers = &point_object_handlers;

    return retval;
}

zend_object_value polygon_create_handler(zend_class_entry* type TSRMLS_DC) {
    zval* tmp;
    zend_object_value retval;

    polygon_object* obj = (polygon_object*)emalloc(sizeof(polygon_object));
    memset(obj, 0, sizeof(polygon_object));
    obj->std.ce = type;

    ALLOC_HASHTABLE(obj->std.properties);
    zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
    zend_hash_copy(obj->std.properties, &type->default_properties, (copy_ctor_func_t)zval_add_ref, (void*)&tmp, sizeof(zval*));

    retval.handle = zend_objects_store_put(obj, NULL,
        polygon_free_storage, NULL TSRMLS_CC);
    retval.handlers = &polygon_object_handlers;

    return retval;
}

function_entry point_methods[] = {
    PHP_ME(Point, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
    PHP_ME(Point, x,           NULL, ZEND_ACC_PUBLIC)
    PHP_ME(Point, y,           NULL, ZEND_ACC_PUBLIC)
    {NULL, NULL, NULL}
};

function_entry polygon_methods[] = {
    PHP_ME(Polygon, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
    PHP_ME(Polygon, add,         NULL, ZEND_ACC_PUBLIC)
    PHP_ME(Polygon, get,         NULL, ZEND_ACC_PUBLIC)
    PHP_ME(Polygon, size,        NULL, ZEND_ACC_PUBLIC)
    {NULL, NULL, NULL}
};

PHP_MINIT_FUNCTION(geometry) {
    zend_class_entry ce;

    INIT_CLASS_ENTRY(ce, "Point", point_methods);
    point_ce = zend_register_internal_class(&ce TSRMLS_CC);
    point_ce->create_object = point_create_handler;
    memcpy(&point_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
    point_object_handlers.clone_obj = NULL;

    INIT_CLASS_ENTRY(ce, "Polygon", polygon_methods);
    polygon_ce = zend_register_internal_class(&ce TSRMLS_CC);
    polygon_ce->create_object = polygon_create_handler;
    memcpy(&polygon_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
    polygon_object_handlers.clone_obj = NULL;

    return SUCCESS;
}

zend_module_entry geometry_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_GEOMETRY_EXTNAME,
    NULL,        /* Functions */
    PHP_MINIT(geometry),        /* MINIT */
    NULL,        /* MSHUTDOWN */
    NULL,        /* RINIT */
    NULL,        /* RSHUTDOWN */
    NULL,        /* MINFO */
#if ZEND_MODULE_API_NO >= 20010901
    PHP_GEOMETRY_EXTVER,
#endif
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_GEOMETRY
extern "C" {
ZEND_GET_MODULE(geometry)
}
#endif

PHP_METHOD(Point, __construct) {
    double x = .0;
    double y = .0;
    zval *object = getThis();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|dd", &x, &y) == FAILURE)
        RETURN_NULL();

    point_object *obj = (point_object *)zend_object_store_get_object(object TSRMLS_CC);
    obj->point = new Point(x, y);
}

PHP_METHOD(Point, x) {
    point_object *obj = (point_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
    Point *point = obj->point;

    if(point != NULL)
        RETURN_DOUBLE(point->x());
}

PHP_METHOD(Point, y) {
    point_object *obj = (point_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
    Point *point = obj->point;

    if(point != NULL)
        RETURN_DOUBLE(point->y());
}

PHP_METHOD(Polygon, __construct) {
    zval* object = getThis();
    polygon_object* obj = (polygon_object*)zend_object_store_get_object(object TSRMLS_CC);
    obj->polygon = new Polygon;
}

PHP_METHOD(Polygon, add) {
    polygon_object *obj = (polygon_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
    Polygon *polygon = obj->polygon;

    if(polygon != NULL) {
        zval* oth;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &oth, point_ce) == FAILURE)
            RETURN_NULL();

        point_object* ooth = (point_object*)zend_object_store_get_object(oth TSRMLS_CC);

        polygon->add(*ooth->point);
    }
}

PHP_METHOD(Polygon, get) {
    polygon_object *obj = (polygon_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
    Polygon *polygon = obj->polygon;

    if(polygon != NULL) {
        long index;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) == FAILURE)
            RETURN_NULL();

        if (object_init_ex(return_value, point_ce) != SUCCESS) {
        } else {
            struct point_object* vobj = (struct point_object *) zend_object_store_get_object(return_value TSRMLS_CC);
            assert (vobj != NULL);
            vobj->point = &polygon->get(index);
        }
    }
}

PHP_METHOD(Polygon, size) {
    polygon_object *obj = (polygon_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
    Polygon *polygon = obj->polygon;

    if(polygon != NULL)
        RETURN_LONG(polygon->size());
}

然后,在编译之后,尝试以下PHP代码:
<?php
echo '<pre>';

$pt = new Point;

var_dump($pt);

$pl = new Polygon;
$pl->add($pt);
$pl->add(new Point(10, 0));
$pl->add(new Point(10, 10));

for($i = 0; $i < $pl->size(); $i++)
    var_dump($pl->get($i));
?>

在执行Polygon::get方法时始终会崩溃,如果我对其进行注释,则不会发生任何错误。

最佳答案

Valgrind给我这个:

==14188== Invalid free() / delete / delete[]
==14188==    at 0x4C27A83: operator delete(void*) (vg_replace_malloc.c:387)
==14188==    by 0xE1C3D7C: point_free_storage(void*) (geometry.cpp:24)

因此,在某些时候,这一行:
delete obj->point;

...在obj->point不是实际的指针时执行。删除delete调用也很可能会消除该错误(尽管很可能会发生泄漏,因为在某些情况下obj->point将是指针)。

从我可以看到,Polygon::get不返回指针,而是返回引用。由于它不是指针,因此在其上调用delete会导致Invalid free()错误。

关于php - 面向对象的C++ PHP扩展,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6348370/

10-11 18:08