/*
* Copyright (c) 1998-2010 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.QuercusRuntimeException;
import com.caucho.quercus.expr.ClassConstExpr;
import com.caucho.quercus.expr.Expr;
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.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.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;
// TODO: 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
// TODO: 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
// TODO: 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
*/
@Override
public boolean isA(String name) {
return _instanceofSet.contains(name.toLowerCase());
}
/*
* 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()) {
Value field = qThis.getField(env, name);
if (field != null && field.isset()) {
return BooleanValue.TRUE; // TODO: move to ObjectExtValue if possible
}
}
// 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());
*/
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;
}
@Override
public int hashCode() {
return _className.hashCode();
}
@Override
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));
}
}
@Override
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;
}
}