/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.quercus.env;
import com.caucho.quercus.QuercusException;
import com.caucho.quercus.QuercusRuntimeException;
import com.caucho.quercus.expr.ClassConstExpr;
import com.caucho.quercus.expr.Expr;
import com.caucho.quercus.expr.LiteralStringExpr;
import com.caucho.quercus.module.ModuleContext;
import com.caucho.quercus.function.AbstractFunction;
import com.caucho.quercus.program.ClassDef;
import com.caucho.quercus.program.InstanceInitializer;
import com.caucho.quercus.program.JavaClassDef;
import com.caucho.util.IntMap;
import com.caucho.util.L10N;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Represents a Quercus runtime class.
*/
public class QuercusClass extends NullValue {
private static final L10N L = new L10N(QuercusClass.class);
private static final Logger log
= Logger.getLogger(QuercusClass.class.getName());
private final JavaClassDef _javaClassDef;
private final ClassDef _classDef;
private final String _className;
private QuercusClass _parent;
private WeakReference<QuercusClass> _cacheRef;
private boolean _isAbstract;
private boolean _isInterface;
private boolean _isJavaWrapper;
private ClassDef []_classDefList;
private AbstractFunction _constructor;
private AbstractFunction _destructor;
private AbstractFunction _fieldGet;
private AbstractFunction _fieldSet;
private AbstractFunction _call;
private AbstractFunction _invoke;
private AbstractFunction _toString;
private AbstractFunction _isset;
private AbstractFunction _unset;
private ArrayDelegate _arrayDelegate;
private TraversableDelegate _traversableDelegate;
private CountDelegate _countDelegate;
private final ArrayList<InstanceInitializer> _initializers;
private final MethodMap<AbstractFunction> _methodMap;
private final HashMap<String,Expr> _constMap;
private final HashMap<String,Object> _constJavaMap;
private final LinkedHashMap<StringValue,ClassField> _fieldMap;
private final HashMap<String,ArrayList<StaticField>> _staticFieldExprMap;
private final HashMap<StringValue,StringValue> _staticFieldNameMap;
private final HashSet<String> _instanceofSet;
private boolean _isModified;
public QuercusClass(ClassDef classDef, QuercusClass parent)
{
this(ModuleContext
.getLocalContext(Thread.currentThread().getContextClassLoader()),
classDef,
parent);
}
public QuercusClass(ModuleContext moduleContext,
ClassDef classDef,
QuercusClass parent)
{
_classDef = classDef.loadClassDef(); // force load of any lazy classes
_className = classDef.getName();
_parent = parent;
_isAbstract = _classDef.isAbstract();
_isInterface = _classDef.isInterface();
_initializers = new ArrayList<InstanceInitializer>();
_fieldMap = new LinkedHashMap<StringValue,ClassField>();
_methodMap = new MethodMap<AbstractFunction>(this, null);
_constMap = new HashMap<String,Expr>();
_constJavaMap = new HashMap<String,Object>();
_staticFieldExprMap = new LinkedHashMap<String,ArrayList<StaticField>>();
_staticFieldNameMap = new LinkedHashMap<StringValue,StringValue>();
if (parent != null) {
_staticFieldNameMap.putAll(parent._staticFieldNameMap);
}
JavaClassDef javaClassDef = null;
if (classDef instanceof JavaClassDef) {
javaClassDef = (JavaClassDef) classDef;
_isJavaWrapper = ! javaClassDef.isDelegate();
}
for (QuercusClass cls = parent; cls != null; cls = cls.getParent()) {
AbstractFunction cons = cls.getConstructor();
if (cons != null) {
addMethod(new StringBuilderValue(cls.getName()), cons);
}
}
ClassDef []classDefList;
if (_parent != null) {
classDefList = new ClassDef[parent._classDefList.length + 1];
System.arraycopy(parent._classDefList, 0, classDefList, 1,
parent._classDefList.length);
classDefList[0] = classDef;
}
else {
classDefList = new ClassDef[] { classDef };
}
_classDefList = classDefList;
for (int i = 0; i < classDefList.length; i++) {
if (classDefList[i] instanceof JavaClassDef)
javaClassDef = (JavaClassDef) classDefList[i];
}
_javaClassDef = javaClassDef;
_instanceofSet = new HashSet<String>();
HashSet<String> ifaces = new HashSet<String>();
// add interfaces
for (int i = classDefList.length - 1; i >= 0; i--) {
classDef = classDefList[i];
if (classDef == null) {
throw new NullPointerException("classDef:" + _classDef
+ " i:" + i + " parent:" + parent);
}
classDef.init();
addInstances(_instanceofSet, ifaces, classDef);
}
// then add concrete ancestors
for (int i = classDefList.length - 1; i >= 0; i--) {
classDef = classDefList[i];
classDef.initClass(this);
}
if (_constructor == null && parent != null)
_constructor = parent.getConstructor();
// php/093n
if (_constructor != null
&& ! _constructor.getName().equals("__construct")) {
addMethodIfNotExist(new StringBuilderValue("__construct"), _constructor);
addMethodIfNotExist(new StringBuilderValue(_className), _constructor);
}
if (_destructor == null && parent != null)
_destructor = parent.getDestructor();
}
private void addInstances(HashSet<String> instanceofSet,
HashSet<String> ifaces,
ClassDef classDef)
{
// _instanceofSet.add(classDef.getName());
classDef.addInterfaces(instanceofSet);
for (String iface : classDef.getInterfaces()) {
boolean isJavaClassDef = classDef instanceof JavaClassDef;
QuercusClass cl;
// XXX: php/0cn2, but this is wrong:
cl = Env.getInstance().findClass(iface,
! isJavaClassDef,
true);
if (cl == null)
throw new QuercusRuntimeException(L.l("cannot find interface {0}",
iface));
// _instanceofSet.addAll(cl.getInstanceofSet());
ClassDef ifaceDef = cl.getClassDef();
// ClassDef ifaceDef = moduleContext.findClass(iface);
if (ifaceDef != null) {
if (ifaces.add(iface)) {
addInstances(instanceofSet, ifaces, ifaceDef);
ifaceDef.initClass(this);
}
}
}
}
/**
* Copy based on a cached value
*/
public QuercusClass(QuercusClass cacheClass, QuercusClass parent)
{
_cacheRef = new WeakReference<QuercusClass>(cacheClass);
_javaClassDef = cacheClass._javaClassDef;
_classDef = cacheClass._classDef;
_className = cacheClass._className;
_isJavaWrapper = cacheClass._isJavaWrapper;
_classDefList = cacheClass._classDefList;
_parent = parent;
_constructor = cacheClass._constructor;
_destructor = cacheClass._destructor;
_fieldGet = cacheClass._fieldGet;
_fieldSet = cacheClass._fieldSet;
_call = cacheClass._call;
_invoke = cacheClass._invoke;
_toString = cacheClass._toString;
_arrayDelegate = cacheClass._arrayDelegate;
_traversableDelegate = cacheClass._traversableDelegate;
_countDelegate = cacheClass._countDelegate;
_initializers = cacheClass._initializers;
_fieldMap = cacheClass._fieldMap;
_methodMap = cacheClass._methodMap;
_constMap = cacheClass._constMap;
_constJavaMap = cacheClass._constJavaMap;
_staticFieldExprMap = cacheClass._staticFieldExprMap;
_staticFieldNameMap = cacheClass._staticFieldNameMap;
_instanceofSet = cacheClass._instanceofSet;
}
public ClassDef getClassDef()
{
return _classDef;
}
public JavaClassDef getJavaClassDef()
{
return _javaClassDef;
}
public MethodMap<AbstractFunction> getMethodMap()
{
return _methodMap;
}
public HashSet<String> getInstanceofSet()
{
return _instanceofSet;
}
/**
* Returns the name.
*/
public String getName()
{
return _className;
}
/**
* Returns the parent class.
*/
public QuercusClass getParent()
{
return _parent;
}
/*
* Returns the class definitions for this class.
*/
public ClassDef []getClassDefList()
{
return _classDefList;
}
/*
* Returns the name of the extension that this class is part of.
*/
public String getExtension()
{
return _classDef.getExtension();
}
public boolean isInterface()
{
return _isInterface;
}
public boolean isAbstract()
{
return _isAbstract;
}
public boolean isFinal()
{
return _classDef.isFinal();
}
/**
* Sets the constructor.
*/
public void setConstructor(AbstractFunction fun)
{
_constructor = fun;
}
/**
* Gets the constructor.
*/
public AbstractFunction getConstructor()
{
return _constructor;
}
/**
* Sets the destructor.
*/
public void setDestructor(AbstractFunction fun)
{
_destructor = fun;
}
/**
* Gets the destructor.
*/
public AbstractFunction getDestructor()
{
return _destructor;
}
/**
* Returns true if the class is modified for caching.
*/
public boolean isModified()
{
if (_isModified)
return true;
else if (_parent != null)
return _parent.isModified();
else
return false;
}
/**
* Mark the class as modified for caching.
*/
public void setModified()
{
if (! _isModified) {
_isModified = true;
if (_cacheRef != null) {
QuercusClass cacheClass = _cacheRef.get();
if (cacheClass != null)
cacheClass.setModified();
}
}
}
/**
* Sets the array delegate (see ArrayAccess)
*/
public void setArrayDelegate(ArrayDelegate delegate)
{
if (log.isLoggable(Level.FINEST))
log.log(Level.FINEST, L.l("{0} adding array delegate {1}",
this, delegate));
_arrayDelegate = delegate;
}
/**
* Gets the array delegate (see ArrayAccess)
*/
public final ArrayDelegate getArrayDelegate()
{
return _arrayDelegate;
}
/**
* Sets the traversable delegate
*/
public void setTraversableDelegate(TraversableDelegate delegate)
{
if (log.isLoggable(Level.FINEST))
log.log(Level.FINEST, L.l("{0} setting traversable delegate {1}",
this, delegate));
_traversableDelegate = delegate;
}
/**
* Gets the traversable delegate
*/
public final TraversableDelegate getTraversableDelegate()
{
return _traversableDelegate;
}
/**
* Sets the count delegate
*/
public void setCountDelegate(CountDelegate delegate)
{
if (log.isLoggable(Level.FINEST))
log.log(Level.FINEST, L.l("{0} setting count delegate {1}",
this, delegate));
_countDelegate = delegate;
}
/**
* Gets the count delegate
*/
public final CountDelegate getCountDelegate()
{
return _countDelegate;
}
/**
* Sets the __fieldGet
*/
public void setFieldGet(AbstractFunction fun)
{
_fieldGet = fun;
}
/**
* Returns the __fieldGet
*/
public AbstractFunction getFieldGet()
{
return _fieldGet;
}
/**
* Sets the __fieldSet
*/
public void setFieldSet(AbstractFunction fun)
{
_fieldSet = fun;
}
/**
* Returns the __fieldSet
*/
public AbstractFunction getFieldSet()
{
return _fieldSet;
}
/**
* Sets the __call
*/
public void setCall(AbstractFunction fun)
{
_call = fun;
}
/**
* Gets the __call
*/
public AbstractFunction getCall()
{
return _call;
}
/**
* Sets the __invoke
*/
public void setInvoke(AbstractFunction fun)
{
_invoke = fun;
}
/**
* Gets the __invoke
*/
public AbstractFunction getInvoke()
{
return _invoke;
}
/**
* Sets the __toString
*/
public void setToString(AbstractFunction fun)
{
_toString = fun;
}
/**
* Gets the __toString
*/
public AbstractFunction getToString()
{
return _toString;
}
/**
* Adds an initializer
*/
public void addInitializer(InstanceInitializer init)
{
_initializers.add(init);
}
/**
* Adds a field.
*/
public void addField(StringValue name,
Expr initExpr,
FieldVisibility visibility)
{
ClassField field = new ClassField(name, initExpr, visibility);
_fieldMap.put(name, field);
}
/**
* Returns a set of the fields and their initial values
*/
public HashMap<StringValue,ClassField> getClassFields()
{
return _fieldMap;
}
/**
* Returns a set of the fields and their initial values
*/
public ClassField getClassField(StringValue name)
{
return _fieldMap.get(name);
}
/**
* Returns a set of the fields and their initial values
*/
public int findFieldIndex(StringValue name)
{
throw new UnsupportedOperationException();
}
/**
* Returns the declared functions.
*/
public Iterable<AbstractFunction> getClassMethods()
{
return _methodMap.values();
}
/**
* Adds a method.
*/
public void addMethod(String name, AbstractFunction fun)
{
addMethod(new StringBuilderValue(name), fun);
}
/**
* Adds a method.
*/
public void addMethod(StringValue name, AbstractFunction fun)
{
if (fun == null)
throw new NullPointerException(L.l("'{0}' is a null function", name));
//php/09j9
// XXX: this is a hack to get Zend Framework running, the better fix is
// to initialize all interface classes before any concrete classes
AbstractFunction existingFun = _methodMap.getRaw(name);
if (existingFun == null || ! fun.isAbstract())
_methodMap.put(name.toString(), fun);
else if (! existingFun.isAbstract() && fun.isAbstract())
Env.getInstance()
.error(L.l("cannot make non-abstract function {0}:{1}() abstract",
getName(), name));
}
/*
* Adds a method if it does not exist.
*/
public void addMethodIfNotExist(StringValue name, AbstractFunction fun)
{
if (fun == null)
throw new NullPointerException(L.l("'{0}' is a null function", name));
//php/09j9
// XXX: this is a hack to get Zend Framework running, the better fix is
// to initialize all interface classes before any concrete classes
AbstractFunction existingFun = _methodMap.getRaw(name);
if (existingFun == null && ! fun.isAbstract())
_methodMap.put(name.toString(), fun);
}
/**
* Adds a static class field.
*/
public void addStaticFieldExpr(String className, String name, Expr value)
{
ArrayList<StaticField> fieldList = _staticFieldExprMap.get(className);
if (fieldList == null) {
fieldList = new ArrayList<StaticField>();
_staticFieldExprMap.put(className, fieldList);
}
fieldList.add(new StaticField(name, value));
_staticFieldNameMap.put(new ConstStringValue(name),
new ConstStringValue(className + "::" + name));
}
/**
* Returns the static field names.
*/
public ArrayList<StringValue> getStaticFieldNames()
{
ArrayList<StringValue> names = new ArrayList<StringValue>();
if (_staticFieldExprMap != null) {
for (StringValue fieldName : _staticFieldNameMap.keySet()) {
names.add(fieldName);
}
}
return names;
}
/**
* Adds a constant definition
*/
public void addConstant(String name, Expr expr)
{
_constMap.put(name, expr);
}
/**
* Adds a constant definition
*/
public void addJavaConstant(String name, Object obj)
{
_constJavaMap.put(name, obj);
}
/**
* Returns the number of fields.
*/
public int getFieldSize()
{
return _fieldMap.size();
}
public void validate(Env env)
{
if (! _isAbstract && ! _isInterface) {
for (AbstractFunction fun : _methodMap.values()) {
/* XXX: abstract methods need to be validated
php/393g, php/393i, php/39j2
if (! (absFun instanceof Function))
continue;
Function fun = (Function) absFun;
*/
boolean isAbstract;
// php/093g constructor
if (_constructor != null
&& fun.getName().equals(_constructor.getName()))
isAbstract = _constructor.isAbstract();
else
isAbstract = fun.isAbstract();
if (isAbstract) {
throw env.createErrorException(
_classDef.getLocation(),
L.l(
"Abstract function '{0}' must be "
+ "implemented in concrete class {1}.",
fun.getName(),
getName()));
}
}
}
}
public void init(Env env)
{
if (_staticFieldExprMap.size() == 0)
return;
for (Map.Entry<String,ArrayList<StaticField>> map
: _staticFieldExprMap.entrySet()) {
if (env.isInitializedClass(map.getKey()))
continue;
for (StaticField field : map.getValue()) {
Value val;
Expr expr = field._expr;
//php/096f
if (expr instanceof ClassConstExpr)
val = ((ClassConstExpr) expr).eval(env);
else
val = expr.eval(env);
StringValue fullName = env.createStringBuilder();
fullName.append(_className);
fullName.append("::");
fullName.append(field._name);
env.setStaticRef(fullName, val);
}
env.addInitializedClass(map.getKey());
}
}
public Value getStaticFieldValue(Env env, StringValue name)
{
StringValue staticName = _staticFieldNameMap.get(name);
if (staticName == null) {
env.error(L.l("{0}::${1} is an undeclared static field",
_className, name));
return NullValue.NULL;
}
return env.getStaticValue(staticName);
}
public Var getStaticFieldVar(Env env, StringValue name)
{
StringValue staticName = _staticFieldNameMap.get(name);
if (staticName == null) {
env.error(L.l("{0}::${1} is an undeclared static field",
_className, name));
throw new IllegalStateException();
}
return env.getStaticVar(staticName);
}
public Value setStaticFieldRef(Env env, StringValue name, Value value)
{
StringValue staticName = _staticFieldNameMap.get(name);
if (staticName == null) {
env.error(L.l("{0}::{1} is an unknown static field",
_className, name));
throw new IllegalStateException();
}
return env.setStaticRef(staticName, value);
}
/**
* For Reflection.
*/
public Value getStaticField(Env env, StringValue name)
{
StringValue staticName = _staticFieldNameMap.get(name);
if (staticName != null)
return env.getStaticValue(staticName);
else
return null;
}
//
// Constructors
//
/**
* Creates a new instance.
*/
/*
public Value callNew(Env env, Expr []args)
{
Value object = _classDef.callNew(env, args);
if (object != null)
return object;
object = newInstance(env);
AbstractFunction fun = findConstructor();
if (fun != null) {
fun.callMethod(env, object, args);
}
return object;
}
*/
/**
* Creates a new object without calling the constructor. This is used
* for unserializing classes.
*/
public Value createObject(Env env)
{
if (_isAbstract) {
throw env.createErrorException(L.l(
"abstract class '{0}' cannot be instantiated.",
_className));
}
else if (_isInterface) {
throw env.createErrorException(L.l(
"interface '{0}' cannot be instantiated.",
_className));
}
ObjectValue objectValue = null;
if (_isJavaWrapper) {
// Java objects always need to call the constructor?
return _javaClassDef.callNew(env, Value.NULL_ARGS);
}
else if (_javaClassDef != null && _javaClassDef.isDelegate()) {
objectValue = new ObjectExtValue(this);
}
else if (_javaClassDef != null && _javaClassDef.isPhpClass()) {
// Java objects always need to call the constructor?
Value javaWrapper = _javaClassDef.callNew(env, Value.NULL_ARGS);
Object object = javaWrapper.toJavaObject();
objectValue = new ObjectExtJavaValue(this, object, _javaClassDef);
}
else if (_javaClassDef != null && ! _javaClassDef.isDelegate()) {
objectValue = new ObjectExtJavaValue(this, null, _javaClassDef);
}
else {
objectValue = _classDef.createObject(env, this);
}
initObject(env, objectValue);
return objectValue;
}
/**
* Initializes the object's methods and fields.
*/
public void initObject(Env env, ObjectValue obj)
{
for (int i = 0; i < _initializers.size(); i++) {
_initializers.get(i).initInstance(env, obj);
}
}
/**
* Creates a new instance.
*/
public Value callNew(Env env, Value ...args)
{
QuercusClass oldCallingClass = env.setCallingClass(this);
try {
if (_classDef.isAbstract()) {
throw env.createErrorException(L.l(
"abstract class '{0}' cannot be instantiated.",
_className));
}
else if (_classDef.isInterface()) {
throw env.createErrorException(L.l(
"interface '{0}' cannot be instantiated.",
_className));
}
ObjectValue objectValue = null;
if (_isJavaWrapper) {
return _javaClassDef.callNew(env, args);
}
else if (_javaClassDef != null && _javaClassDef.isDelegate()) {
objectValue = new ObjectExtValue(this);
}
else if (_javaClassDef != null && _javaClassDef.isPhpClass()) {
// php/0k3-
Value javaWrapper = _javaClassDef.callNew(env, args);
Object object = javaWrapper.toJavaObject();
objectValue = new ObjectExtJavaValue(this, object, _javaClassDef);
}
else if (_javaClassDef != null && ! _javaClassDef.isDelegate()) {
objectValue = new ObjectExtJavaValue(this, null, _javaClassDef);
}
else {
objectValue = _classDef.newInstance(env, this);
}
initObject(env, objectValue);
AbstractFunction fun = findConstructor();
if (fun != null)
fun.callMethod(env, this, objectValue, args);
else {
// if expr
}
return objectValue;
} finally {
env.setCallingClass(oldCallingClass);
}
}
/**
* Returns the parent class.
*/
public String getParentName()
{
return _classDefList[0].getParentName();
}
/**
* Returns true for an implementation of a class
*/
public boolean isA(String name)
{
return _instanceofSet.contains(name.toLowerCase(Locale.ENGLISH));
}
/*
* Returns an array of the interfaces that this class and its parents
* implements.
*/
public ArrayValue getInterfaces(Env env, boolean autoload)
{
ArrayValue array = new ArrayValueImpl();
getInterfaces(env, array, autoload, true);
return array;
}
/*
* Puts the interfaces that this class and its parents implements
* into the array.
*/
private void getInterfaces(Env env, ArrayValue array,
boolean autoload, boolean isTop)
{
ClassDef [] defList = _classDefList;
for (int i = 0; i < defList.length; i++) {
ClassDef def = defList[i];
if (! isTop && def.isInterface()) {
String name = def.getName();
array.put(name, name);
}
String []defNames = def.getInterfaces();
for (int j = 0; j < defNames.length; j++) {
QuercusClass cls = env.findClass(defNames[j]);
cls.getInterfaces(env, array, autoload, false);
}
}
if (_parent != null)
_parent.getInterfaces(env, array, autoload, false);
}
/*
* Returns true if this class or its parents implements specified interface.
*/
public boolean implementsInterface(Env env, String name)
{
ClassDef [] defList = _classDefList;
for (int i = 0; i < defList.length; i++) {
ClassDef def = defList[i];
if (def.isInterface() && def.getName().equals(name))
return true;
String []defNames = def.getInterfaces();
for (int j = 0; j < defNames.length; j++) {
QuercusClass cls = env.findClass(defNames[j]);
if (cls.implementsInterface(env, name))
return true;
}
}
if (_parent != null)
return _parent.implementsInterface(env, name);
else
return false;
}
/**
* Finds the matching constructor.
*/
public AbstractFunction findConstructor()
{
return _constructor;
}
//
// Fields
//
/**
* Implements the __get method call.
* __get() is utilized for reading data from inaccessible properties.
*/
public Value getField(Env env, Value qThis, StringValue name)
{
// php/09km, php/09kn
// push/pop to prevent infinite recursion
if(issetField(name) && _fieldMap.get(name).isPublic())
{
Value v_current = this.get(name); // TODO: move to ObjectExtValue if possible
if(v_current != NullValue.NULL && v_current != UnsetValue.UNSET)
{
return v_current;
}
if(_fieldGet == null){
return ((ClassField) _fieldMap.get(name)).getInitValue().eval(env);
}
}
if (_fieldGet != null) {
if (! env.pushFieldGet(Env.OVERLOADING_TYPES.FIELDGET, qThis.getClassName(), name))
return UnsetValue.UNSET;
try {
return _fieldGet.callMethod(env, this, qThis, name);
} finally {
env.popFieldGet(Env.OVERLOADING_TYPES.FIELDGET);
}
}
else
return UnsetValue.UNSET;
}
/**
* Implements the __isset method call.
* __isset() is triggered by calling isset() or empty() on inaccessible properties.
*/
public Value issetField(Env env, Value qThis, StringValue name)
{
if(issetField(name) && _fieldMap.get(name).isPublic())
{
// php/09c3
Value v_current = this.get(name); // TODO: move to ObjectExtValue if possible
if(v_current != NullValue.NULL && v_current != UnsetValue.UNSET)
return BooleanValue.TRUE;
}
// basically a copy of the __get code with slightly different semantics
if (_isset != null) {
if (! env.pushFieldGet(Env.OVERLOADING_TYPES.ISSET, qThis.getClassName(), name))
return UnsetValue.UNSET;
try {
return _isset.callMethod(env, this, qThis, name);
} finally {
env.popFieldGet(Env.OVERLOADING_TYPES.ISSET);
}
}
else
{
return UnsetValue.UNSET;
}
}
@Override
public boolean issetField(StringValue name) {
if(_fieldMap.containsKey(name))
return true;
return false;
}
@Override
public void unsetField(StringValue name) {
if(_fieldMap.containsKey(name))
_fieldMap.remove(name);
}
/**
* implements the __unset method call
* __unset() is invoked when unset() is used on inaccessible properties.
*/
public Value unsetField(Env env, Value qThis, StringValue name)
{
if(issetField(name) && _fieldMap.get(name).isPublic()){
// TODO: move to ObjectExtValue if possible
unsetField(name);
return NullValue.NULL;
}
// basically a copy of the __get code with slightly different semantics
if (_unset != null) {
if (! env.pushFieldGet(Env.OVERLOADING_TYPES.UNSET, qThis.getClassName(), name))
return UnsetValue.UNSET;
try {
return _unset.callMethod(env, this, qThis, name);
} finally {
env.popFieldGet(Env.OVERLOADING_TYPES.UNSET);
}
}
else
unsetField(name);
return NullValue.NULL;
}
/**
* Implements the __set method call.
*/
public Value setField(Env env, Value qThis, StringValue name, Value value)
{
if (_fieldSet != null){
if (! env.pushFieldGet(Env.OVERLOADING_TYPES.FIELDSET, qThis.getClassName(), name))
return UnsetValue.UNSET;
try {
return _fieldSet.callMethod(env, this, qThis, name, value);
}
finally {
env.popFieldGet(Env.OVERLOADING_TYPES.FIELDSET);
}
}
return UnsetValue.UNSET;
}
/**
* Finds the matching function.
*/
public AbstractFunction findStaticFunction(String name)
{
return findFunction(name);
}
/**
* Finds the matching function.
*/
public final AbstractFunction getFunction(StringValue methodName)
{
return _methodMap.get(methodName, methodName.hashCodeCaseInsensitive());
}
/**
* Finds the matching function.
*/
@Override
public final AbstractFunction findFunction(String methodName)
{
return _methodMap.getRaw(new StringBuilderValue(methodName));
}
/**
* Finds the matching function.
*/
public final AbstractFunction findFunction(StringValue methodName)
{
return _methodMap.getRaw(methodName);
}
/**
* Finds the matching function.
*/
public final AbstractFunction getFunction(StringValue methodName, int hash)
{
return _methodMap.get(methodName, methodName.hashCode());
/*
AbstractFunction fun = _methodMap.get(methodName, hash);
if (fun != null)
return fun;
else if (_className.equalsIgnoreCase(toMethod(name, nameLen))
&& _parent != null) {
// php/093j
return _parent.getFunction(_parent.getName());
}
else {
throw new QuercusRuntimeException(L.l("{0}::{1} is an unknown method",
getName(), toMethod(name, nameLen)));
}
*/
}
/**
* calls the function.
*/
public Value callMethod(Env env,
Value qThis,
StringValue methodName, int hash,
Value []args)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, this, qThis, args);
}
public final Value callMethod(Env env, Value qThis, StringValue methodName,
Value []args)
{
return callMethod(env, qThis,
methodName, methodName.hashCodeCaseInsensitive(),
args);
}
/**
* calls the function.
*/
public Value callMethod(Env env, Value qThis,
StringValue methodName, int hash)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, this, qThis);
}
public final Value callMethod(Env env, Value qThis, StringValue methodName)
{
return callMethod(env, qThis,
methodName, methodName.hashCodeCaseInsensitive());
}
/**
* calls the function.
*/
public Value callMethod(Env env, Value qThis,
StringValue methodName, int hash,
Value a1)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, this, qThis, a1);
}
public final Value callMethod(Env env, Value qThis, StringValue methodName,
Value a1)
{
return callMethod(env, qThis,
methodName, methodName.hashCodeCaseInsensitive(),
a1);
}
/**
* calls the function.
*/
public Value callMethod(Env env, Value qThis,
StringValue methodName, int hash,
Value a1, Value a2)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, this, qThis, a1, a2);
}
public final Value callMethod(Env env, Value qThis, StringValue methodName,
Value a1, Value a2)
{
return callMethod(env, qThis,
methodName, methodName.hashCodeCaseInsensitive(),
a1, a2);
}
/**
* calls the function.
*/
public Value callMethod(Env env, Value qThis,
StringValue methodName, int hash,
Value a1, Value a2, Value a3)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, this, qThis, a1, a2, a3);
}
public final Value callMethod(Env env, Value qThis, StringValue methodName,
Value a1, Value a2, Value a3)
{
return callMethod(env, qThis,
methodName, methodName.hashCodeCaseInsensitive(),
a1, a2, a3);
}
/**
* calls the function.
*/
public Value callMethod(Env env, Value qThis,
StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, this, qThis, a1, a2, a3, a4);
}
public final Value callMethod(Env env, Value qThis, StringValue methodName,
Value a1, Value a2, Value a3, Value a4)
{
return callMethod(env, qThis,
methodName, methodName.hashCodeCaseInsensitive(),
a1, a2, a3, a4);
}
/**
* calls the function.
*/
public Value callMethod(Env env, Value qThis,
StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4, Value a5)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, this, qThis, a1, a2, a3, a4, a5);
}
public final Value callMethod(Env env, Value qThis, StringValue methodName,
Value a1, Value a2, Value a3, Value a4,
Value a5)
{
return callMethod(env, qThis,
methodName, methodName.hashCodeCaseInsensitive(),
a1, a2, a3, a4, a5);
}
/**
* calls the function.
*/
public Value callMethodRef(Env env, Value qThis,
StringValue methodName, int hash,
Value []args)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, this, qThis, args);
}
public final Value callMethodRef(Env env, Value qThis, StringValue methodName,
Value []args)
{
return callMethodRef(env, qThis,
methodName, methodName.hashCodeCaseInsensitive(),
args);
}
/**
* calls the function.
*/
public Value callMethodRef(Env env, Value qThis,
StringValue methodName, int hash)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, this, qThis);
}
public final Value callMethodRef(Env env, Value qThis, StringValue methodName)
{
return callMethodRef(env, qThis,
methodName, methodName.hashCodeCaseInsensitive());
}
/**
* calls the function.
*/
public Value callMethodRef(Env env, Value qThis,
StringValue methodName, int hash,
Value a1)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, this, qThis, a1);
}
public final Value callMethodRef(Env env, Value qThis, StringValue methodName,
Value a1)
{
return callMethodRef(env, qThis,
methodName, methodName.hashCodeCaseInsensitive(),
a1);
}
/**
* calls the function.
*/
public Value callMethodRef(Env env, Value qThis,
StringValue methodName, int hash,
Value a1, Value a2)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, this, qThis, a1, a2);
}
public final Value callMethodRef(Env env, Value qThis, StringValue methodName,
Value a1, Value a2)
{
return callMethodRef(env, qThis,
methodName, methodName.hashCodeCaseInsensitive(),
a1, a2);
}
/**
* calls the function.
*/
public Value callMethodRef(Env env, Value qThis,
StringValue methodName, int hash,
Value a1, Value a2, Value a3)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, this, qThis, a1, a2, a3);
}
public final Value callMethodRef(Env env, Value qThis, StringValue methodName,
Value a1, Value a2, Value a3)
{
return callMethodRef(env, qThis,
methodName, methodName.hashCodeCaseInsensitive(),
a1, a2, a3);
}
/**
* calls the function.
*/
public Value callMethodRef(Env env, Value qThis,
StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, this, qThis,
a1, a2, a3, a4);
}
public final Value callMethodRef(Env env, Value qThis, StringValue methodName,
Value a1, Value a2, Value a3, Value a4)
{
return callMethodRef(env, qThis,
methodName, methodName.hashCodeCaseInsensitive(),
a1, a2, a3, a4);
}
/**
* calls the function.
*/
public Value callMethodRef(Env env, Value qThis,
StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4, Value a5)
{
if (qThis.isNull())
qThis = this;
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, this, qThis,
a1, a2, a3, a4, a5);
}
public final Value callMethodRef(Env env, Value qThis, StringValue methodName,
Value a1, Value a2, Value a3, Value a4,
Value a5)
{
return callMethodRef(env, qThis,
methodName, methodName.hashCodeCaseInsensitive(),
a1, a2, a3, a4, a5);
}
//
// Static method calls
//
/**
* calls the function.
*/
/*
private Value callStaticMethod(Env env,
Value thisValue,
StringValue methodName,
Expr []args)
{
QuercusClass oldClass = env.setCallingClass(this);
try {
return callMethod(env, thisValue, methodName, args);
} finally {
env.setCallingClass(oldClass);
}
}*/
/**
* calls the function.
*/
@Override
public Value callMethod(Env env,
StringValue methodName, int hash,
Value []args)
{
return callMethod(env, this, methodName, hash, args);
}
/**
* calls the function.
*/
@Override
public Value callMethod(Env env,
StringValue methodName, int hash)
{
return callMethod(env, this, methodName, hash);
}
/**
* calls the function.
*/
@Override
public Value callMethod(Env env,
StringValue methodName, int hash,
Value a1)
{
return callMethod(env, this, methodName, hash,
a1);
}
/**
* calls the function.
*/
@Override
public Value callMethod(Env env,
StringValue methodName, int hash,
Value a1, Value a2)
{
return callMethod(env, this, methodName, hash,
a1, a2);
}
/**
* calls the function.
*/
@Override
public Value callMethod(Env env,
StringValue methodName, int hash,
Value a1, Value a2, Value a3)
{
return callMethod(env, this, methodName, hash,
a1, a2, a3);
}
/**
* calls the function.
*/
@Override
public Value callMethod(Env env,
StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4)
{
return callMethod(env, this, methodName, hash,
a1, a2, a3, a4);
}
/**
* calls the function.
*/
@Override
public Value callMethod(Env env,
StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4,
Value a5)
{
return callMethod(env, this, methodName, hash,
a1, a2, a3, a4, a5);
}
/**
* calls the function.
*/
@Override
public Value callMethodRef(Env env,
StringValue methodName, int hash,
Value []args)
{
return callMethodRef(env, this, methodName, hash, args);
}
/**
* calls the function.
*/
@Override
public Value callMethodRef(Env env,
StringValue methodName, int hash)
{
return callMethodRef(env, this, methodName, hash);
}
/**
* calls the function.
*/
@Override
public Value callMethodRef(Env env,
StringValue methodName, int hash,
Value a1)
{
return callMethodRef(env, this, methodName, hash,
a1);
}
/**
* calls the function.
*/
@Override
public Value callMethodRef(Env env,
StringValue methodName, int hash,
Value a1, Value a2)
{
return callMethodRef(env, this, methodName, hash,
a1, a2);
}
/**
* calls the function.
*/
@Override
public Value callMethodRef(Env env,
StringValue methodName, int hash,
Value a1, Value a2, Value a3)
{
return callMethodRef(env, this, methodName, hash,
a1, a2, a3);
}
/**
* calls the function.
*/
@Override
public Value callMethodRef(Env env,
StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4)
{
return callMethodRef(env, this, methodName, hash,
a1, a2, a3, a4);
}
/**
* calls the function.
*/
@Override
public Value callMethodRef(Env env,
StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4,
Value a5)
{
return callMethodRef(env, this, methodName, hash,
a1, a2, a3, a4, a5);
}
private String toMethod(char []key, int keyLength)
{
return new String(key, 0, keyLength);
}
/**
* Finds a function.
*/
public AbstractFunction findStaticFunctionLowerCase(String name)
{
return null;
}
/**
* Finds the matching function.
*/
public final AbstractFunction getStaticFunction(String name)
{
AbstractFunction fun = findStaticFunction(name);
/*
if (fun != null)
return fun;
fun = findStaticFunctionLowerCase(name.toLowerCase(Locale.ENGLISH));
*/
if (fun != null)
return fun;
else {
throw new QuercusRuntimeException(L.l("{0}::{1} is an unknown method",
getName(), name));
}
}
/**
* Finds the matching constant
*/
public final Value getConstant(Env env, String name)
{
Expr expr = _constMap.get(name);
if (expr != null)
return expr.eval(env);
Object obj = _constJavaMap.get(name);
if (obj != null)
return env.wrapJava(obj);
throw new QuercusRuntimeException(L.l("{0}::{1} is an unknown constant",
getName(), name));
}
/**
* Returns true if the constant exists.
*/
public final boolean hasConstant(String name)
{
if (_constMap.get(name) != null)
return true;
else
return _constJavaMap.get(name) != null;
}
/**
* Returns the constants defined in this class.
*/
public final HashMap<String, Value> getConstantMap(Env env)
{
HashMap<String, Value> map = new HashMap<String, Value>();
for (Map.Entry<String, Expr> entry : _constMap.entrySet()) {
map.put(entry.getKey(), entry.getValue().eval(env));
}
for (Map.Entry<String, Object> entry : _constJavaMap.entrySet()) {
map.put(entry.getKey(), env.wrapJava(entry.getValue()));
}
return map;
}
//
// Value methods
//
@Override
public boolean isNull()
{
return false;
}
/**
* Returns the value's class name.
*/
@Override
public String getClassName()
{
return getName();
}
@Override
public QuercusClass getQuercusClass()
{
return this;
}
public int hashCode()
{
return _className.hashCode();
}
public boolean equals(Object o)
{
if (this == o)
return true;
else if (! (o instanceof QuercusClass))
return false;
QuercusClass qClass = (QuercusClass) o;
if (_classDef != qClass._classDef)
return false;
if (_javaClassDef != qClass._javaClassDef)
return false;
if (_parent == qClass._parent)
return true;
else
return (_parent != null && _parent.equals(qClass._parent));
}
public String toString()
{
return getClass().getSimpleName() + "[" + getName() + "]";
}
static class StaticField
{
String _name;
Expr _expr;
StaticField(String name, Expr expr)
{
_name = name;
_expr = expr;
}
String getName()
{
return _name;
}
}
public void setIsset(AbstractFunction isset)
{
_isset = isset;
}
public void setUnset(AbstractFunction unset)
{
_unset = unset;
}
public AbstractFunction getIsset()
{
return _isset;
}
public AbstractFunction getUnset()
{
return _unset;
}
}