Skip to content

Commit

Permalink
Add basic reflection support for type constants
Browse files Browse the repository at this point in the history
Summary: I added ReflectionClass::getTypeConstantInfo that dumps out information about
all the type constants in a class. Right now it is an array containing its
name, whether or not its abstract, and what type it aliases (null if it is
abstract).

I also filter out type constants from the reflection methods for constants such
as ReflectionClass::getConstants(),
ReflectionClass::getAbstractConstantNames(), get_class_constants(), etc.

Reviewed By: @elgenie

Differential Revision: D1914280
  • Loading branch information
dlreeves authored and hhvm-bot committed Mar 20, 2015
1 parent b2224b2 commit fcdc18a
Show file tree
Hide file tree
Showing 10 changed files with 424 additions and 4 deletions.
15 changes: 15 additions & 0 deletions hphp/hack/hhi/reflection.hhi
Expand Up @@ -34,6 +34,8 @@ class ReflectionClass implements Reflector {
public function getConstant(string $name): mixed;
public function getConstants(): array<string, mixed>;
public function getAbstractConstantNames(): array<string, string>;
public function getTypeConstant(string $name): ReflectionTypeConstant;
public function getTypeConstants(): array<ReflectionTypeConstant>;
public function getConstructor(): ?ReflectionMethod;
public function getDefaultProperties(): array<string, mixed>;
/**
Expand Down Expand Up @@ -76,6 +78,7 @@ class ReflectionClass implements Reflector {
public function hasConstant(string $name): bool;
public function hasMethod(string $name): bool;
public function hasProperty(string $name): bool;
public function hasTypeConstant(string $name): bool;
public function implementsInterface(string $interface): bool;
public function inNamespace(): bool;
public function isAbstract(): bool;
Expand Down Expand Up @@ -277,3 +280,15 @@ class ReflectionZendExtension implements Reflector {
public function getURL();
public function getCopyright();
}

class ReflectionTypeConstant implements Reflector {

final private function __clone();
public static function export($class, string $name, $return = null);
public function __construct($class, string $name);
public function __toString(): string;
public function getName(): string;
public function isAbstract(): bool;
public function getDeclaringClass(): ReflectionClass;
public function getAssignedTypeText(): ?string;
}
98 changes: 96 additions & 2 deletions hphp/runtime/ext/reflection/ext_reflection.cpp
Expand Up @@ -1200,7 +1200,8 @@ void addClassConstantNames(const Class* cls,

const Class::Const* consts = cls->constants();
for (size_t i = 0; i < numConsts; i++) {
if (consts[i].m_class == cls && !consts[i].isAbstract()) {
if (consts[i].m_class == cls && !consts[i].isAbstract() &&
!consts[i].isType()) {
st->add(const_cast<StringData*>(consts[i].m_name.get()));
}
}
Expand Down Expand Up @@ -1254,7 +1255,32 @@ static Array HHVM_METHOD(ReflectionClass, getOrderedAbstractConstants) {

const Class::Const* consts = cls->constants();
for (size_t i = 0; i < numConsts; i++) {
if (consts[i].isAbstract()) {
if (consts[i].isAbstract() && !consts[i].isType()) {
st->add(const_cast<StringData*>(consts[i].m_name.get()));
}
}

assert(st->size() <= numConsts);
return st->t_toarray();
}



// helper for getTypeConstants/hasTypeConstant
static Array HHVM_METHOD(ReflectionClass, getOrderedTypeConstants) {
auto const cls = ReflectionClassHandle::GetClassFor(this_);

size_t numConsts = cls->numConstants();
if (!numConsts) {
return Array::Create();
}

auto st = makeSmartPtr<c_Set>();
st->reserve(numConsts);

const Class::Const* consts = cls->constants();
for (size_t i = 0; i < numConsts; i++) {
if (consts[i].isType()) {
st->add(const_cast<StringData*>(consts[i].m_name.get()));
}
}
Expand Down Expand Up @@ -1433,6 +1459,65 @@ struct reflection_extension_PropHandler :
reflection_extension_accessorsMap;
};

/////////////////////////////////////////////////////////////////////////////
// class ReflectionTypeConstant

const StaticString s_ReflectionConstHandle("ReflectionConstHandle");

// helper for __construct
static bool HHVM_METHOD(ReflectionTypeConstant, __init,
const Variant& cls_or_obj, const String& const_name) {
auto const cls = get_cls(cls_or_obj);
if (!cls || const_name.isNull()) {
// caller raises exception
return false;
}

size_t numConsts = cls->numConstants();
const Class::Const* consts = cls->constants();

for (size_t i = 0; i < numConsts; i++) {
if (consts[i].m_name == const_name.get() && consts[i].isType()) {
ReflectionConstHandle::Get(this_)->setConst(&consts[i]);
return true;
}
}

// caller raises exception
return false;
}

static String HHVM_METHOD(ReflectionTypeConstant, getName) {
auto const cst = ReflectionConstHandle::GetConstFor(this_);
auto ret = const_cast<StringData*>(cst->m_name.get());
return String(ret);
}

static bool HHVM_METHOD(ReflectionTypeConstant, isAbstract) {
auto const cst = ReflectionConstHandle::GetConstFor(this_);
return cst->isAbstract();
}

// helper for getAssignedTypeText
static String HHVM_METHOD(ReflectionTypeConstant, getAssignedTypeHint) {
auto const cst = ReflectionConstHandle::GetConstFor(this_);

if (cst->m_val.m_type == KindOfStaticString ||
cst->m_val.m_type == KindOfString) {
return String(cst->m_val.m_data.pstr);
}

return String();
}

// private helper for getDeclaringClass
static String HHVM_METHOD(ReflectionTypeConstant, getDeclaringClassname) {
auto const cst = ReflectionConstHandle::GetConstFor(this_);
auto cls = cst->m_class;
auto ret = const_cast<StringData*>(cls->name());
return String(ret);
}

///////////////////////////////////////////////////////////////////////////////

class ReflectionExtension final : public Extension {
Expand Down Expand Up @@ -1485,6 +1570,12 @@ class ReflectionExtension final : public Extension {
HHVM_ME(ReflectionFunction, getClosureScopeClassname);
HHVM_ME(ReflectionFunction, getClosureThisObject);

HHVM_ME(ReflectionTypeConstant, __init);
HHVM_ME(ReflectionTypeConstant, getName);
HHVM_ME(ReflectionTypeConstant, isAbstract);
HHVM_ME(ReflectionTypeConstant, getAssignedTypeHint);
HHVM_ME(ReflectionTypeConstant, getDeclaringClassname);

HHVM_ME(ReflectionClass, __init);
HHVM_ME(ReflectionClass, getName);
HHVM_ME(ReflectionClass, getParentName);
Expand Down Expand Up @@ -1513,6 +1604,7 @@ class ReflectionExtension final : public Extension {
HHVM_ME(ReflectionClass, getConstant);
HHVM_ME(ReflectionClass, getOrderedConstants);
HHVM_ME(ReflectionClass, getOrderedAbstractConstants);
HHVM_ME(ReflectionClass, getOrderedTypeConstants);

HHVM_ME(ReflectionClass, getAttributes);
HHVM_ME(ReflectionClass, getAttributesRecursive);
Expand All @@ -1525,6 +1617,8 @@ class ReflectionExtension final : public Extension {
s_ReflectionFuncHandle.get());
Native::registerNativeDataInfo<ReflectionClassHandle>(
s_ReflectionClassHandle.get());
Native::registerNativeDataInfo<ReflectionConstHandle>(
s_ReflectionConstHandle.get());

Native::registerNativePropHandler
<reflection_extension_PropHandler>(s_reflectionextension);
Expand Down
34 changes: 34 additions & 0 deletions hphp/runtime/ext/reflection/ext_reflection.h
Expand Up @@ -126,6 +126,40 @@ class ReflectionClassHandle {
const Class* m_cls{nullptr};
};

/* A ReflectionConstHandle is a NativeData object wrapping a Const*
* for the purposes of ReflectionTypeConstant. */
extern const StaticString s_ReflectionConstHandle;
class ReflectionConstHandle {
public:
ReflectionConstHandle(): m_const(nullptr) {}
explicit ReflectionConstHandle(const Class::Const* cst): m_const(cst) {};
ReflectionConstHandle(const ReflectionConstHandle&) = delete;
ReflectionConstHandle& operator=(const ReflectionConstHandle& other) {
m_const = other.m_const;
return *this;
}
~ReflectionConstHandle() {}

static ReflectionConstHandle* Get(ObjectData* obj) {
return Native::data<ReflectionConstHandle>(obj);
}

static const Class::Const* GetConstFor(ObjectData* obj) {
return Native::data<ReflectionConstHandle>(obj)->getConst();
}

const Class::Const* getConst() { return m_const; }

void setConst(const Class::Const* cst) {
assert(cst != nullptr);
assert(m_const == nullptr);
m_const = cst;
}

private:
const Class::Const* m_const{nullptr};
};

namespace DebuggerReflection {
Array get_function_info(const String& name);
Array get_class_info(const String& name);
Expand Down
125 changes: 125 additions & 0 deletions hphp/runtime/ext/reflection/ext_reflection_hni.php
Expand Up @@ -1508,12 +1508,43 @@ public function getAbstractConstantNames(): array<string, string> {
return self::$absConstCache[$clsname] = $this->getOrderedAbstractConstants();
}

private static $typeConstCache = array();

private function getTypeConstantNamesWithCaching(): array<string, string> {
$clsname = $this->getName();
$cached = hphp_array_idx(self::$typeConstCache, $clsname, null);
if (null !== $cached) {
return $cached;
}
return self::$typeConstCache[$clsname] = $this->getOrderedTypeConstants();
}

public function getTypeConstant(string $name): ReflectionTypeConstant {
return new ReflectionTypeConstant($this->getName(), $name);
}

public function hasTypeConstant($name): bool {
return array_key_exists($name, $this->getTypeConstantNamesWithCaching());
}

public function getTypeConstants(): array<ReflectionTypeConstant> {
$ret = array();
$class = $this->getName();
foreach ($this->getTypeConstantNamesWithCaching() as $name) {
$ret[] = new ReflectionTypeConstant($class, $name);
}
return $ret;
}

<<__Native>>
private function getOrderedConstants(): array<string, mixed>;

<<__Native>>
private function getOrderedAbstractConstants(): array<string, string>;

<<__Native>>
public function getOrderedTypeConstants(): array<string, string>;

/**
* ( excerpt from
* http://docs.hhvm.com/manual/en/reflectionclass.getinterfacenames.php )
Expand Down Expand Up @@ -2178,3 +2209,97 @@ public static function export($obj, $ret=false) {
print $str;
}
}

///////////////////////////////////////////////////////////////////////////////
// type constant

/**
* The ReflectionTypeConstant class reports information about an object.
*
*/
<<__NativeData('ReflectionConstHandle')>>
class ReflectionTypeConstant implements Reflector {

/**
* Constructs a new ReflectionTypeConstant.
*
* @cls mixed Classname or object (instance of the class) that
* contains the type constant.
* @name string Name of the type constant.
*/
public function __construct(mixed $cls, string $name) {
if (!$this->__init($cls, (string) $name)) {
$classname = is_object($cls) ? get_class($cls) : $cls;
throw new ReflectionException(
"Type Constant $classname::$name does not exist");
}
}

/**
* Get the name of the type constant.
*
* @return string The name of the type constant.
*/
<<__Native>>
public function getName(): string;

/**
* Checks if the type constant is abstract
*
* @return bool Returns TRUE on success or FALSE on failure.
*/
<<__Native>>
public function isAbstract(): bool;

/**
* Get the type assigned to this type constant as a string
*
* @return NULL | string The assigned type or null if is abstract
*/
public function getAssignedTypeText(): ?string {
return $this->getAssignedTypeHint() ?: null;
}

/**
* Gets the declaring class for the reflected type constant.
*
* @return ReflectionClass A ReflectionClass object of the class that the
* reflected type constant is part of.
*/
public function getDeclaringClass() {
return new ReflectionClass($this->getDeclaringClassname());
}

public function __toString() {
$abstract = $this->isAbstract() ? 'abstract ' : '';

$val = $this->isAbstract() ? '' : " = {$this->getAssignedTypeText()}";

return "TypeConstant [ {$abstract}const type {$this->getName()}{$val}]\n";
}

// Prevent cloning
final public function __clone() {
throw new BadMethodCallException(
'Trying to clone an uncloneable object of class ReflectionTypeConstant'
);
}

public static function export($cls, $name, $ret=false) {
$obj = new self($cls, $name);
$str = (string)$obj;
if ($ret) {
return $str;
}
print $str;
}

<<__Native>>
private function __init(mixed $cls_or_obj, string $const): bool;

<<__Native>>
private function getAssignedTypeHint(): string;

<<__Native>>
private function getDeclaringClassname(): string;
}
3 changes: 2 additions & 1 deletion hphp/runtime/ext/std/ext_std_classobj.cpp
Expand Up @@ -128,7 +128,8 @@ Array HHVM_FUNCTION(get_class_constants, const String& className) {
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 && !consts[i].isAbstract()) {
if (consts[i].m_class == cls && !consts[i].isAbstract() &&
!consts[i].isType()) {
auto const name = const_cast<StringData*>(consts[i].m_name.get());
Cell value = consts[i].m_val;
// Handle dynamically set constants
Expand Down
3 changes: 2 additions & 1 deletion hphp/runtime/vm/class-inl.h
Expand Up @@ -281,7 +281,8 @@ inline bool Class::hasConstant(const StringData* clsCnsName) const {
// m_constants.contains(clsCnsName) returns abstract constants
auto clsCnsInd = m_constants.findIndex(clsCnsName);
return (clsCnsInd != kInvalidSlot) &&
!m_constants[clsCnsInd].isAbstract();
!m_constants[clsCnsInd].isAbstract() &&
!m_constants[clsCnsInd].isType();
}

///////////////////////////////////////////////////////////////////////////////
Expand Down

0 comments on commit fcdc18a

Please sign in to comment.