/* * 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; } }