fa6f3f7e12
Throws the aliased class into a target cache slot for the new
name. Handles errors when you try to re-alias a class, but doesn't
restrict a few other cases zend does:
- If you implement an interface twice, zend complains (one of the
alias tests checks this). I tried turning it on, but we violate
it in systemlib currently so I left it off.
- class_alias_014.php does some namespace stuff I don't quite grok.
(@ptarjan let me know what to do if it's easy).
- inter_007.php uses class_alias, but is testing a warning that
happens even with out it. (We don't raise this warning.)
- zend raises a warning if you try to class_alias a non-user-defined
class; I left this out.
325 linhas
10 KiB
C++
325 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->nvSet(name, 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->nvSet(name, 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->nvSet(const_cast<StringData*>(sPropInfo[i].m_name), 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(CVarRef object) {
|
|
if (object.isObject()) {
|
|
return object.toObject()->o_toIterArray(ctxClassName());
|
|
}
|
|
raise_warning("get_object_vars() expects parameter 1 to be object");
|
|
return Variant(Variant::NullInit());
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
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);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|