Arquivos
hhvm/hphp/runtime/ext/ext_class.cpp
T
Paul Tarjan 506f21c4b5 Allow extension functions to match zend calling convention
Introducing `ZendParamMode` to as a idl flag. We are not consistent with zend on how they do their params for builtins. We cast to the expected data type. They do some checks, and if the checks don't pass they issue a warning and return (usually) `null`. This diff starts us down that path.

I'm introducing the param and using it in the places where we were emulating the calling convention in the `f_foo` functions. I'm going to follow up with converting as many as I can and then eventually this becomes the default. I also want this to be applied to php files in systemlib.

Many of the conversions are from https://github.com/php/php-src/blob/master/Zend/zend_API.c#L305
2013-06-25 13:19:04 -07:00

322 linhas
10 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
| Copyright (c) 1997-2010 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/ext/ext_class.h"
#include "hphp/runtime/base/class_info.h"
#include "hphp/runtime/vm/jit/translator.h"
#include "hphp/runtime/vm/jit/translator-inline.h"
#include "hphp/runtime/vm/unit.h"
#include "hphp/util/util.h"
namespace HPHP {
using Transl::CallerFrame;
using Transl::VMRegAnchor;
///////////////////////////////////////////////////////////////////////////////
// helpers
static String get_classname(CVarRef class_or_object) {
if (class_or_object.is(KindOfObject)) {
return class_or_object.toCObjRef().get()->o_getClassName();
}
return class_or_object.toString();
}
static inline CStrRef ctxClassName() {
Class* ctx = g_vmContext->getContextClass();
return ctx ? ctx->nameRef() : empty_string;
}
static const Class* get_cls(CVarRef class_or_object) {
Class* cls = NULL;
if (class_or_object.is(KindOfObject)) {
ObjectData* obj = class_or_object.toCObjRef().get();
cls = obj->getVMClass();
} else {
cls = Unit::loadClass(class_or_object.toString().get());
}
return cls;
}
///////////////////////////////////////////////////////////////////////////////
Array f_get_declared_classes() {
return ClassInfo::GetClasses();
}
Array f_get_declared_interfaces() {
return ClassInfo::GetInterfaces();
}
Array f_get_declared_traits() {
return ClassInfo::GetTraits();
}
bool f_class_alias(CStrRef original,
CStrRef alias,
bool autoload /* = true */) {
auto const origClass =
autoload ? Unit::loadClass(original.get())
: Unit::lookupClass(original.get());
if (!origClass) {
raise_warning("Class %s not found", original->data());
return false;
}
return Unit::aliasClass(origClass, alias.get());
}
bool f_class_exists(CStrRef class_name, bool autoload /* = true */) {
return Unit::classExists(class_name.get(), autoload, AttrNone);
}
bool f_interface_exists(CStrRef interface_name, bool autoload /* = true */) {
return Unit::classExists(interface_name.get(), autoload,
AttrInterface);
}
bool f_trait_exists(CStrRef trait_name, bool autoload /* = true */) {
return Unit::classExists(trait_name.get(), autoload, AttrTrait);
}
Array f_get_class_methods(CVarRef class_or_object) {
const Class* cls = get_cls(class_or_object);
if (!cls) return Array();
VMRegAnchor _;
auto retVal = ArrayData::Make(cls->numMethods());
cls->getMethodNames(arGetContextClassFromBuiltin(g_vmContext->getFP()),
retVal);
return Array(retVal).keys();
}
Array vm_get_class_constants(CStrRef className) {
HPHP::Class* cls = HPHP::Unit::loadClass(className.get());
if (cls == NULL) {
return ArrayData::Make(0);
}
size_t numConstants = cls->numConstants();
auto retVal = ArrayData::Make(numConstants);
const Class::Const* consts = cls->constants();
for (size_t i = 0; i < numConstants; i++) {
// Note: hphpc doesn't include inherited constants in
// get_class_constants(), so mimic that behavior
if (consts[i].m_class == cls) {
StringData* name = const_cast<StringData*>(consts[i].m_name);
const TypedValue* value = &consts[i].m_val;
// Handle dynamically set constants
if (value->m_type == KindOfUninit) {
value = cls->clsCnsGet(consts[i].m_name);
}
retVal->set(name, tvAsCVarRef(value), false);
}
}
return retVal;
}
Array f_get_class_constants(CStrRef class_name) {
return vm_get_class_constants(class_name.get());
}
Array vm_get_class_vars(CStrRef className) {
HPHP::Class* cls = HPHP::Unit::lookupClass(className.get());
if (cls == NULL) {
raise_error("Unknown class %s", className->data());
}
cls->initialize();
const Class::SProp* sPropInfo = cls->staticProperties();
const size_t numSProps = cls->numStaticProperties();
const Class::Prop* propInfo = cls->declProperties();
const size_t numDeclProps = cls->numDeclProperties();
// The class' instance property initialization template is in different
// places, depending on whether it has any request-dependent initializers
// (i.e. constants)
const Class::PropInitVec& declPropInitVec = cls->declPropInit();
const Class::PropInitVec* propVals = !cls->pinitVec().empty()
? cls->getPropData() : &declPropInitVec;
assert(propVals != NULL);
assert(propVals->size() == numDeclProps);
// For visibility checks
CallerFrame cf;
HPHP::Class* ctx = arGetContextClass(cf());
auto ret = ArrayData::Make(numDeclProps + numSProps);
for (size_t i = 0; i < numDeclProps; ++i) {
StringData* name = const_cast<StringData*>(propInfo[i].m_name);
// Empty names are used for invisible/private parent properties; skip them
assert(name->size() != 0);
if (Class::IsPropAccessible(propInfo[i], ctx)) {
const TypedValue* value = &((*propVals)[i]);
ret->set(name, tvAsCVarRef(value), false);
}
}
for (size_t i = 0; i < numSProps; ++i) {
bool vis, access;
TypedValue* value = cls->getSProp(ctx, sPropInfo[i].m_name, vis, access);
if (access) {
ret->set(const_cast<StringData*>(sPropInfo[i].m_name),
tvAsCVarRef(value), false);
}
}
return ret;
}
Array f_get_class_vars(CStrRef class_name) {
return vm_get_class_vars(class_name.get());
}
///////////////////////////////////////////////////////////////////////////////
Variant f_get_class(CVarRef object /* = null_variant */) {
if (object.isNull()) {
// No arg passed.
String ret;
CallerFrame cf;
HPHP::Class* cls = arGetContextClassImpl<true>(cf());
if (cls) {
ret = CStrRef(cls->nameRef());
}
if (ret.empty()) {
raise_warning("get_class() called without object from outside a class");
return false;
}
return ret;
}
if (!object.isObject()) return false;
return object.toObject()->o_getClassName();
}
Variant f_get_parent_class(CVarRef object /* = null_variant */) {
if (!object.isInitialized()) {
CallerFrame cf;
HPHP::Class* cls = arGetContextClass(cf());
if (cls && cls->parent()) {
return CStrRef(cls->parentRef());
}
return false;
}
Variant class_name;
if (object.isObject()) {
class_name = f_get_class(object);
} else if (object.isString()) {
class_name = object;
} else {
return false;
}
Class* cls = Unit::lookupClass(class_name.toString().get());
if (cls) {
CStrRef parentClass = *(const String*)(&cls->parentRef());
if (!parentClass.empty()) {
return parentClass;
}
}
return false;
}
static bool is_a_impl(CVarRef class_or_object, CStrRef class_name,
bool allow_string, bool subclass_only) {
if (class_or_object.isString() && !allow_string) {
return false;
}
const Class* cls = get_cls(class_or_object);
if (!cls) return false;
if (cls->attrs() & AttrTrait) return false;
const Class* other = Unit::lookupClass(class_name.get());
if (!other) return false;
if (other->attrs() & AttrTrait) return false;
if (other == cls) return !subclass_only;
return cls->classof(other);
}
bool f_is_a(CVarRef class_or_object, CStrRef class_name, bool allow_string /* = false */) {
return is_a_impl(class_or_object, class_name, allow_string, false);
}
bool f_is_subclass_of(CVarRef class_or_object, CStrRef class_name, bool allow_string /* = true */) {
return is_a_impl(class_or_object, class_name, allow_string, true);
}
bool f_method_exists(CVarRef class_or_object, CStrRef method_name) {
const Class* cls = get_cls(class_or_object);
if (!cls) return false;
if (cls->lookupMethod(method_name.get()) != NULL) return true;
if (cls->attrs() & AttrAbstract) {
const Class::InterfaceMap& ifaces = cls->allInterfaces();
for (int i = 0, size = ifaces.size(); i < size; i++) {
if (ifaces[i]->lookupMethod(method_name.get())) return true;
}
}
return false;
}
Variant f_property_exists(CVarRef class_or_object, CStrRef property) {
if (class_or_object.isObject()) {
CStrRef context = ctxClassName();
return (bool)class_or_object.toObject()->o_realProp(
property, ObjectData::RealPropExist, context);
}
if (!class_or_object.isString()) {
raise_warning(
"First parameter must either be an object or the name of an existing class"
);
return Variant(Variant::NullInit());
}
Class* cls = Unit::lookupClass(get_classname(class_or_object).get());
if (!cls) {
return false;
}
bool accessible;
auto propInd = cls->getDeclPropIndex(cls, property.get(), accessible);
if (propInd != kInvalidSlot) {
return true;
}
propInd = cls->lookupSProp(property.get());
return (propInd != kInvalidSlot);
}
Variant f_get_object_vars(CObjRef object) {
return object->o_toIterArray(ctxClassName());
}
///////////////////////////////////////////////////////////////////////////////
Variant f_call_user_method_array(CStrRef method_name, VRefParam obj,
CArrRef paramarr) {
return obj.toObject()->o_invoke(method_name, paramarr);
}
Variant f_call_user_method(int _argc, CStrRef method_name, VRefParam obj,
CArrRef _argv /* = null_array */) {
return obj.toObject()->o_invoke(method_name, _argv);
}
///////////////////////////////////////////////////////////////////////////////
}