Skip to content

Commit

Permalink
add ClassCallParentPassRefArg php test
Browse files Browse the repository at this point in the history
  • Loading branch information
zzusoftboy committed Sep 8, 2017
1 parent f12c11d commit 07942c6
Show file tree
Hide file tree
Showing 13 changed files with 217 additions and 20 deletions.
30 changes: 25 additions & 5 deletions include/zapi/lang/StdClass.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,6 @@ class ZAPI_DECL_EXPORT StdClass

public:
virtual ~StdClass();

Variant callParent();
Variant callStaticParent();
ObjectVariant *getThisPtr() const;
/**
* Get access to a property by name using the [] operator
* @param string
Expand Down Expand Up @@ -237,7 +233,13 @@ class ZAPI_DECL_EXPORT StdClass
* @return int
*/
int __compare(const StdClass &object) const;

protected:
template <typename ...Args>
Variant callParent(const char *name, Args&&... args);
template <typename ...Args>
Variant callParent(const char *name, Args&&... args) const;
private:
zval *doCallParent(const char *name, const int argc, Variant *argv, zval *retval) const;
protected:
ZAPI_DECLARE_PRIVATE(StdClass)
friend class Variant;// for Variant(const StdClass &stdClass);
Expand All @@ -246,6 +248,24 @@ class ZAPI_DECL_EXPORT StdClass
std::unique_ptr<StdClassPrivate> m_implPtr;
};

template <typename ...Args>
Variant StdClass::callParent(const char *name, Args&&... args) const
{
Variant vargs[] = { Variant(std::forward<Args>(args))... };
zval retval;
std::memset(&retval, 0, sizeof(retval));
doCallParent(name, sizeof...(Args), vargs, &retval);
Variant resultVarint(retval);
zval_dtor(&retval);
return resultVarint;
}

template <typename ...Args>
Variant StdClass::callParent(const char *name, Args&&... args)
{
return const_cast<const StdClass &>(*this).callParent(name, std::forward<Args>(args)...);
}

} // lang
} // zapi

Expand Down
8 changes: 5 additions & 3 deletions src/ds/BoolVariant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ BoolVariant::BoolVariant(zval *other, bool isRef)
{
zval *self = getUnDerefZvalPtr();
if (nullptr != other) {
if (isRef && ((Z_TYPE_P(other) == IS_TRUE || Z_TYPE_P(other) == IS_FALSE) ||
(Z_TYPE_P(other) == IS_REFERENCE &&
(Z_TYPE_P(Z_REFVAL_P(other)) == IS_TRUE || Z_TYPE_P(Z_REFVAL_P(other)) == IS_FALSE)))) {
if ((isRef && ((Z_TYPE_P(other) == IS_TRUE || Z_TYPE_P(other) == IS_FALSE) ||
(Z_TYPE_P(other) == IS_REFERENCE &&
(Z_TYPE_P(Z_REFVAL_P(other)) == IS_TRUE || Z_TYPE_P(Z_REFVAL_P(other)) == IS_FALSE)))) ||
(!isRef && (Z_TYPE_P(other) == IS_REFERENCE &&
(Z_TYPE_P(Z_REFVAL_P(other)) == IS_TRUE || Z_TYPE_P(Z_REFVAL_P(other)) == IS_FALSE)))) {
ZVAL_MAKE_REF(other);
zend_reference *ref = Z_REF_P(other);
++GC_REFCOUNT(ref);
Expand Down
5 changes: 3 additions & 2 deletions src/ds/DoubleVariant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ DoubleVariant::DoubleVariant(zval *other, bool isRef)
{
zval *self = getUnDerefZvalPtr();
if (nullptr != other) {
if (isRef && (Z_TYPE_P(other) == IS_DOUBLE ||
(Z_TYPE_P(other) == IS_REFERENCE && Z_TYPE_P(Z_REFVAL_P(other)) == IS_DOUBLE))) {
if ((isRef && (Z_TYPE_P(other) == IS_DOUBLE ||
(Z_TYPE_P(other) == IS_REFERENCE && Z_TYPE_P(Z_REFVAL_P(other)) == IS_DOUBLE))) ||
(!isRef && (Z_TYPE_P(other) == IS_REFERENCE && Z_TYPE_P(Z_REFVAL_P(other)) == IS_DOUBLE))) {
ZVAL_MAKE_REF(other);
zend_reference *ref = Z_REF_P(other);
++GC_REFCOUNT(ref);
Expand Down
6 changes: 4 additions & 2 deletions src/ds/NumericVariant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ NumericVariant::NumericVariant(zval *other, bool isRef)
{
zval *self = getUnDerefZvalPtr();
if (nullptr != other) {
if (isRef && (Z_TYPE_P(other) == IS_LONG ||
(Z_TYPE_P(other) == IS_REFERENCE && Z_TYPE_P(Z_REFVAL_P(other)) == IS_LONG))) {
if ((isRef && (Z_TYPE_P(other) == IS_LONG ||
(Z_TYPE_P(other) == IS_REFERENCE && Z_TYPE_P(Z_REFVAL_P(other)) == IS_LONG))) ||
(!isRef && (Z_TYPE_P(other) == IS_REFERENCE && Z_TYPE_P(Z_REFVAL_P(other)) == IS_LONG))) {
// for support pass ref arg when pass variant args
ZVAL_MAKE_REF(other);
zend_reference *ref = Z_REF_P(other);
++GC_REFCOUNT(ref);
Expand Down
6 changes: 5 additions & 1 deletion src/ds/ObjectVariant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,9 +378,13 @@ Variant ObjectVariant::exec(const char *name, int argc, Variant *argv) const
{
Variant methodName(name);
zval params[argc];
zval *curArgPtr = nullptr;
for (int i = 0; i < argc; i++) {
params[i] = *argv[i].getUnDerefZvalPtr();
Z_TRY_ADDREF_P(&params[i]); // _call_user_function_ex free call stack will decrease 1
curArgPtr = &params[i];
if (Z_TYPE_P(curArgPtr) == IS_REFERENCE && Z_REFCOUNTED_P(Z_REFVAL_P(curArgPtr))) {
Z_TRY_ADDREF_P(&params[i]); // _call_user_function_ex free call stack will decrease 1
}
}
return do_execute(getZvalPtr(), methodName.getZvalPtr(), argc, params);
}
Expand Down
5 changes: 3 additions & 2 deletions src/ds/StringVariant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,9 @@ StringVariant::StringVariant(zval *other, bool isRef)
{
zval *self = getUnDerefZvalPtr();
if (nullptr != other && Z_TYPE_P(other) != IS_NULL) {
if (isRef && (Z_TYPE_P(other) == IS_STRING ||
(Z_TYPE_P(other) == IS_REFERENCE && Z_TYPE_P(Z_REFVAL_P(other)) == IS_STRING))) {
if ((isRef && (Z_TYPE_P(other) == IS_STRING ||
(Z_TYPE_P(other) == IS_REFERENCE && Z_TYPE_P(Z_REFVAL_P(other)) == IS_STRING))) ||
(!isRef && (Z_TYPE_P(other) == IS_REFERENCE && Z_TYPE_P(Z_REFVAL_P(other)) == IS_STRING))) {
SEPARATE_STRING(other);
ZVAL_MAKE_REF(other);
zend_reference *ref = Z_REF_P(other);
Expand Down
83 changes: 78 additions & 5 deletions src/lang/StdClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "zapi/lang/StdClass.h"
#include "zapi/lang/internal/StdClassPrivate.h"
#include "zapi/kernel/NotImplemented.h"
#include "zapi/utils/CommonFuncs.h"

namespace zapi
{
Expand All @@ -33,6 +34,7 @@ StdClassPrivate::StdClassPrivate()
} // internal

using zapi::kernel::NotImplemented;
using zapi::lang::internal::StdClassPrivate;

StdClass::StdClass()
: m_implPtr(new StdClassPrivate)
Expand All @@ -45,11 +47,6 @@ StdClass::StdClass(const StdClass &object)
: m_implPtr(new StdClassPrivate)
{}

ObjectVariant *StdClass::getThisPtr() const
{
return nullptr;
}

/**
* Overridable method that is called right before an object is destructed
*/
Expand Down Expand Up @@ -211,5 +208,81 @@ int StdClass::__compare(const StdClass &object) const
throw NotImplemented();
}

zval *StdClass::doCallParent(const char *name, const int argc, Variant *argv, zval *retvalPtr) const
{
zend_object *object = m_implPtr->m_zendObject;
if (!object) {
zapi::error << "invoke StdClass::doCallParent on unbinded nativeObject" << std::endl;
}
int result;
zend_fcall_info fci;
zval retval;
HashTable *funcTable;
zval params[argc];
zval *curArgPtr = nullptr;
for (int i = 0; i < argc; i++) {
params[i] = *argv[i].getUnDerefZvalPtr();
curArgPtr = &params[i];
if (Z_TYPE_P(curArgPtr) == IS_REFERENCE && Z_REFCOUNTED_P(Z_REFVAL_P(curArgPtr))) {
Z_TRY_ADDREF_P(&params[i]); // _call_user_function_ex free call stack will decrease 1
}
}
fci.size = sizeof(fci);
fci.object = object;
ZVAL_STRINGL(&fci.function_name, name, std::strlen(name));
zapi::utils::str_tolower(Z_STRVAL(fci.function_name), Z_STRLEN(fci.function_name));
fci.retval = retvalPtr ? retvalPtr : &retval;
fci.param_count = argc;
fci.params = params;
fci.no_separation = 1;

// setup cache
zend_fcall_info_cache fcic;
fcic.initialized = 1;
zend_class_entry *parentClassType = object->ce->parent;
if (parentClassType) {
funcTable = &parentClassType->function_table;
} else {
funcTable = EG(function_table);
}
if ((fcic.function_handler = reinterpret_cast<zend_function *>(zend_hash_find_ptr(funcTable, Z_STR(fci.function_name)))) == nullptr) {
/* error at c-level */
zend_error(E_CORE_ERROR, "Couldn't find implementation for method %s%s%s",
parentClassType ? ZSTR_VAL(parentClassType->name) : "", parentClassType ? "::" : "", name);
}
fcic.calling_scope = parentClassType;
if (object) {
fcic.called_scope = object->ce;
} else {
zend_class_entry *calledScope = zend_get_called_scope(EG(current_execute_data));

if (parentClassType &&
(!calledScope ||
!instanceof_function(calledScope, parentClassType))) {
fcic.called_scope = parentClassType;
} else {
fcic.called_scope = calledScope;
}
}
fcic.object = object ? object : nullptr;
result = zend_call_function(&fci, &fcic);
zval_ptr_dtor(&fci.function_name);
if (result == ZAPI_FAILURE) {
/* error at c-level */
if (!parentClassType) {
parentClassType = object ? object->ce : nullptr;
}
if (!EG(exception)) {
zend_error(E_CORE_ERROR, "Couldn't execute method %s%s%s", parentClassType
? ZSTR_VAL(parentClassType->name) : "", parentClassType ? "::" : "", name);
}
}
if (!retvalPtr) {
zval_ptr_dtor(&retval);
return nullptr;
}
return retvalPtr;
}

} // lang
} // zapi
1 change: 1 addition & 0 deletions tests/lang/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ set(ZAPI_PHP_TESTS
lang/class/ClassPolymorphicTest.phpt
lang/class/ClassIteratorTest.phpt
lang/class/ClassStaticMethodExistTest.phpt
lang/class/ClassCallParentPassRefArgTest.phpt

lang/class/ClassMethodExistTest.phpt
lang/class/ClassPropertyTest.phpt
Expand Down
15 changes: 15 additions & 0 deletions tests/lang/class/ClassCallParentMethodTest.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Class call parent method test
--FILE--
<?php

if (class_exists("A") && class_exists("B") && class_exists("C")) {
echo "class A and class B and class C exist\n";
$obj = new C();
$obj->printInfo();
}

?>
--EXPECT--
class A and class B and class C exist
C::printInfo been called
14 changes: 14 additions & 0 deletions tests/lang/class/ClassCallParentPassRefArgTest.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
Class call parent method pass ref argument test
--FILE--
<?php

if (class_exists("A") && class_exists("B") && class_exists("C")) {
echo "class A and class B and class C exist\n";
$obj = new C();
$obj->testCallParentPassRefArg();
}

?>
--EXPECT--

47 changes: 47 additions & 0 deletions unittests/extension/ClassDef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ int ObjectVariantClass::calculateSum(NumericVariant argQuantity, ...)
return result;
}


void ObjectVariantClass::changeNameByRef(StringVariant &name)
{
zapi::out << "ObjectVariantClass::changeNameByRef been called" << std::endl;
Expand All @@ -446,12 +447,58 @@ void A::printInfo()
zapi::out << "A::printInfo been called" << std::endl;
}

void A::changeNameByRef(StringVariant &name)
{
zapi::out << "A::changeNameByRef been called" << std::endl;
if (name.getUnDerefType() == Type::Reference) {
zapi::out << "get ref arg" << std::endl;
}
name = "hello, zapi";
}

void B::printInfo()
{
zapi::out << "B::printInfo been called" << std::endl;
}

void B::showSomething()
{
zapi::out << "B::showSomething been called" << std::endl;

}

void B::calculateSumByRef(NumericVariant argQuantity, NumericVariant retval, ...)
{
zapi::out << "C::calculateSumByRef been called" << std::endl;
zapi::out << "got " << argQuantity << " args" << std::endl;
if (retval.getUnDerefType() == Type::Reference) {
zapi::out << "retval is reference arg" << std::endl;
}
va_list args;
va_start(args, retval);
for (int i = 0; i < argQuantity - 1; ++i) {
retval += NumericVariant(va_arg(args, zapi_varidic_item_type), false);
}
}

void C::printInfo()
{
zapi::out << "C::printInfo been called" << std::endl;
callParent("printInfo");
callParent("showSomething");
}

void C::testCallParentPassRefArg()
{
zapi::out << "C::testCallParentPassRefArg been called" << std::endl;
Variant str("xxxx");
zapi::out << "before call changeNameByRef : " << str << std::endl;
callParent("changeNameByRef", str.makeReferenceByZval());
zapi::out << "after call changeNameByRef : " << str << std::endl;
// pass arg when variant args
NumericVariant ret(0);
zapi::out << "before call calculateSumByRef : " << ret.toLong() << std::endl;
callParent("calculateSumByRef", ret.makeReferenceByZval(), 12, 2, 33);
zapi::out << "after call calculateSumByRef : " << ret.toLong() << std::endl;

}
5 changes: 5 additions & 0 deletions unittests/extension/ClassDef.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,18 +120,23 @@ class A : public StdClass
{
public:
void printInfo();
void changeNameByRef(StringVariant &name);
};

class B : public StdClass
{
public:
void printInfo();
void showSomething();
void calculateSumByRef(NumericVariant argQuantity, NumericVariant retval, ...);

};

class C : public StdClass
{
public:
void printInfo();
void testCallParentPassRefArg();
};

#endif // ZAPI_UNITTEST_BRIDGE_DUMMYEXT_CLASS_DEF_H
12 changes: 12 additions & 0 deletions unittests/extension/ExtFuncs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,20 @@ void register_inherit_cls(Extension &extension)
zapi::lang::Class<B> b("B");
zapi::lang::Class<C> c("C");
a.registerMethod<decltype(&A::printInfo), &A::printInfo>("printInfo");
a.registerMethod<decltype(&A::changeNameByRef), &A::changeNameByRef>
("changeNameByRef", {
RefArgument("name", Type::String)
});

b.registerMethod<decltype(&B::printInfo), &B::printInfo>("printInfo");
b.registerMethod<decltype(&B::showSomething), &B::showSomething>("showSomething");
b.registerMethod<decltype(&B::calculateSumByRef), &B::calculateSumByRef>
("calculateSumByRef", {
RefArgument("result", Type::Long),
VariadicArgument("numbers", Type::Long)
});
c.registerMethod<decltype(&C::printInfo), &C::printInfo>("printInfo");
c.registerMethod<decltype(&C::testCallParentPassRefArg), &C::testCallParentPassRefArg>("testCallParentPassRefArg");
b.registerBaseClass(a);
c.registerBaseClass(b);
extension.registerClass(a);
Expand Down

0 comments on commit 07942c6

Please sign in to comment.