/* * Copyright (c) 1998-2008 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; import com.caucho.config.ConfigException; import com.caucho.quercus.annotation.ClassImplementation; import com.caucho.quercus.env.*; import com.caucho.quercus.expr.ExprFactory; import com.caucho.quercus.function.AbstractFunction; import com.caucho.quercus.lib.db.JavaSqlDriverWrapper; import com.caucho.quercus.lib.file.FileModule; import com.caucho.quercus.lib.regexp.RegexpModule; import com.caucho.quercus.lib.session.QuercusSessionManager; import com.caucho.quercus.module.*; import com.caucho.quercus.page.InterpretedPage; import com.caucho.quercus.page.PageManager; import com.caucho.quercus.page.QuercusPage; import com.caucho.quercus.parser.QuercusParser; import com.caucho.quercus.program.*; import com.caucho.util.*; import com.caucho.vfs.*; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.sql.DataSource; import java.io.IOException; import java.sql.Connection; import java.util.*; import java.util.concurrent.*; /** * Facade for the PHP language. */ public class Quercus { private static L10N L = new L10N(Quercus.class); private static HashSet<String> _superGlobals = new HashSet<String>(); private static IniDefinitions _ini = new IniDefinitions(); private final PageManager _pageManager; private final QuercusSessionManager _sessionManager; private final ClassLoader _loader; private ModuleContext _moduleContext; private LruCache<String, UnicodeBuilderValue> _unicodeMap = new LruCache<String, UnicodeBuilderValue>(8 * 1024); private LruCache<String, ConstStringValue> _stringMap = new LruCache<String, ConstStringValue>(8 * 1024); private HashMap<String, ModuleInfo> _modules = new HashMap<String, ModuleInfo>(); private HashSet<ModuleStartupListener> _moduleStartupListeners = new HashSet<ModuleStartupListener>(); private HashSet<String> _extensionSet = new HashSet<String>(); private HashSet<String> _extensionSetLowerCase = new HashSet<String>(); private HashMap<String, AbstractFunction> _funMap = new HashMap<String, AbstractFunction>(); private HashMap<String, AbstractFunction> _lowerFunMap = new HashMap<String, AbstractFunction>(); /* private ClassDef _stdClassDef; private QuercusClass _stdClass; */ private ConcurrentHashMap<String, JavaClassDef> _javaClassWrappers = new ConcurrentHashMap<String, JavaClassDef>(); private LruCache<String, String> _classNotFoundCache = new LruCache<String, String>(64); private HashMap<String, JavaClassDef> _lowerJavaClassWrappers = new HashMap<String, JavaClassDef>(); private final IniDefinitions _iniDefinitions = new IniDefinitions(); private Path _iniFile; private HashMap<String, Value> _iniMap; private HashMap<Value, Value> _serverEnvMap = new HashMap<Value, Value>(); private IntMap _classNameMap = new IntMap(8192); private String []_classNames = new String[256]; private ClassDef []_classDefMap = new ClassDef[256]; private QuercusClass []_classCacheMap = new QuercusClass[256]; private IntMap _constantNameMap = new IntMap(8192); private int []_constantLowerMap = new int[256]; private Value []_constantNameList = new Value[256]; private Value []_constantMap = new Value[256]; // protected to allow locking from pro protected IntMap _functionNameMap = new IntMap(8192); private AbstractFunction []_functionMap = new AbstractFunction[256]; private LruCache<String, QuercusProgram> _evalCache = new LruCache<String, QuercusProgram>(4096); private int _includeCacheMax = 8192; private long _includeCacheTimeout = 10000L; private TimedCache<IncludeKey, Path> _includeCache; //private LruCache<DefinitionKey,SoftReference<DefinitionState>> _defCache // = new LruCache<DefinitionKey,SoftReference<DefinitionState>>(4096); private long _defCacheHitCount; private long _defCacheMissCount; // XXX: needs to be a timed LRU //private LruCache<String, SessionArrayValue> _sessionMap // = new LruCache<String, SessionArrayValue>(4096); private ConcurrentHashMap<String, Object> _specialMap = new ConcurrentHashMap<String, Object>(); private String _scriptEncoding; private String _phpVersion = "5.2.0"; private String _mySqlVersion; private StringValue _phpVersionValue; private boolean _isStrict; private boolean _isRequireSource; private boolean _isConnectionPool = true; private Boolean _isUnicodeSemantics; private DataSource _database; private ConcurrentHashMap<String,DataSource> _databaseMap = new ConcurrentHashMap<String,DataSource>(); private long _staticId; private Path _pwd; private Path _workDir; private ServletContext _servletContext; private boolean _isProduction; /** * Constructor. */ public Quercus(boolean isProduction) { _loader = Thread.currentThread().getContextClassLoader(); _moduleContext = getLocalContext(); _pageManager = createPageManager(); _pageManager.setAutoreloadingEnabled(!isProduction); _sessionManager = createSessionManager(); _isProduction = isProduction; } public Quercus() { this(true); } /** * Returns the working directory. */ public Path getPwd() { if (_pwd == null) _pwd = new FilePath(System.getProperty("user.dir")); return _pwd; } /** * Sets the working directory. */ public void setPwd(Path path) { _pwd = path; } public Path getWorkDir() { if (_workDir == null) _workDir = getPwd().lookup("WEB-INF/work"); return _workDir; } public void setWorkDir(Path workDir) { _workDir = workDir; } public String getCookieName() { return "JSESSIONID"; } public long getDependencyCheckInterval() { return 2000L; } public int getIncludeCacheMax() { return _includeCacheMax; } public void setIncludeCacheMax(int cacheMax) { _includeCacheMax = cacheMax; } public void setIncludeCacheTimeout(long timeout) { _includeCacheTimeout = timeout; } public long getIncludeCacheTimeout() { return _includeCacheTimeout; } public String getVersion() { return "Open Source 4.0.0"; } public String getVersionDate() { return "20090301T2777"; } public boolean isProfile() { return false; } protected PageManager createPageManager() { return new PageManager(this); } protected QuercusSessionManager createSessionManager() { return new QuercusSessionManager(); } /** * Returns the context for this class loader. */ public final ModuleContext getLocalContext() { return getLocalContext(_loader); } public ModuleContext getLocalContext(ClassLoader loader) { if (_moduleContext == null) { synchronized (this) { if (_moduleContext == null) { _moduleContext = createModuleContext(null, loader); _moduleContext.init(); } } } return _moduleContext; } protected ModuleContext createModuleContext(ModuleContext parent, ClassLoader loader) { return new ModuleContext(this, parent, loader); } /** * Returns the module context. */ public ModuleContext getModuleContext() { return _moduleContext; } public QuercusSessionManager getQuercusSessionManager() { return _sessionManager; } /** * true if the pages should be compiled. */ public boolean isCompile() { return _pageManager.isCompile(); } /* * Returns true if this is the Professional version. */ public boolean isPro() { return false; } /* * Returns true if Quercus is running under Resin. */ public boolean isResin() { return false; } /** * Returns true if unicode.semantics is on. */ public boolean isUnicodeSemantics() { if (_isUnicodeSemantics == null) { _isUnicodeSemantics = Boolean.valueOf(getIniBoolean("unicode.semantics")); } return _isUnicodeSemantics.booleanValue(); } /* * Returns true if URLs may be arguments of include(). */ public boolean isAllowUrlInclude() { return getIniBoolean("allow_url_include"); } /* * Returns true if URLs may be arguments of fopen(). */ public boolean isAllowUrlFopen() { return getIniBoolean("allow_url_fopen"); } /** * Set true if pages should be compiled. */ public void setCompile(boolean isCompile) { _pageManager.setCompile(isCompile); } /** * Set true if pages should be compiled. */ public void setLazyCompile(boolean isCompile) { _pageManager.setLazyCompile(isCompile); } /* * true if interpreted pages should be used if pages fail to compile. */ public void setCompileFailover(boolean isCompileFailover) { _pageManager.setCompileFailover(isCompileFailover); } /* * Returns the expected encoding of php scripts. */ public String getScriptEncoding() { if (_scriptEncoding != null) return _scriptEncoding; else if (isUnicodeSemantics()) return "utf-8"; else return "iso-8859-1"; } /* * Sets the expected encoding of php scripts. */ public void setScriptEncoding(String encoding) { _scriptEncoding = encoding; } /* * Returns the mysql version to report to to PHP applications. * It is user set-able to allow cloaking of the underlying mysql * JDBC driver version for application compatibility. */ public String getMysqlVersion() { return _mySqlVersion; } /* * Sets the mysql version to report to applications. This cloaks * the underlying JDBC driver version, so that when an application * asks for the mysql version, this version string is returned instead. */ public void setMysqlVersion(String version) { _mySqlVersion = version; } public String getPhpVersion() { return _phpVersion; } public void setPhpVersion(String version) { _phpVersion = version; _phpVersionValue = null; } public StringValue getPhpVersionValue() { if (_phpVersionValue == null) { if (isUnicodeSemantics()) _phpVersionValue = createUnicodeString(_phpVersion); else _phpVersionValue = createString(_phpVersion); } return _phpVersionValue; } /* * Sets the ServletContext. */ public void setServletContext(ServletContext servletContext) { _servletContext = servletContext; } /* * Returns the ServletContext. */ public ServletContext getServletContext() { return _servletContext; } /** * Sets the default data source. */ public void setDatabase(DataSource database) { _database = database; } /** * Gets the default data source. */ public DataSource getDatabase() { return _database; } /** * Gets the default data source. */ public DataSource findDatabase(String driver, String url) { if (_database != null) return _database; else { try { String key = driver + ";" + url; DataSource database = _databaseMap.get(key); if (database != null) return database; ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class cls = loader.loadClass(driver); Object ds = cls.newInstance(); if (ds instanceof DataSource) database = (DataSource) ds; else database = new JavaSqlDriverWrapper((java.sql.Driver) ds, url); _databaseMap.put(key, database); return database; } catch (ClassNotFoundException e) { throw new QuercusModuleException(e); } catch (InstantiationException e) { throw new QuercusModuleException(e); } catch (IllegalAccessException e) { throw new QuercusModuleException(e); } } } /* * Marks the connection for removal from the connection pool. */ public void markForPoolRemoval(Connection conn) { } /** * Unwrap connection if necessary. */ public Connection getConnection(Connection conn) { return conn; } /** * Unwrap statement if necessary. */ public java.sql.Statement getStatement(java.sql.Statement stmt) { return stmt; } /** * Sets the strict mode. */ public void setStrict(boolean isStrict) { _isStrict = isStrict; } /** * Gets the strict mode. */ public boolean isStrict() { return _isStrict; } /* * Gets the max size of the page cache. */ public int getPageCacheSize() { return _pageManager.getPageCacheSize(); } /* * Sets the capacity of the page cache. */ public void setPageCacheSize(int size) { _pageManager.setPageCacheSize(size); } /* * Gets the max size of the regexp cache. */ public int getRegexpCacheSize() { return RegexpModule.getRegexpCacheSize(); } /* * Sets the capacity of the regexp cache. */ public void setRegexpCacheSize(int size) { RegexpModule.setRegexpCacheSize(size); } /* * Set to true if compiled pages need to be backed by php source files. */ public void setRequireSource(boolean isRequireSource) { _isRequireSource = isRequireSource; } /* * Returns whether the php source is required for compiled files. */ public boolean isRequireSource() { return _isRequireSource; } /* * Turns connection pooling on or off. */ public void setConnectionPool(boolean isEnable) { _isConnectionPool = isEnable; } /* * Returns true if connections should be pooled. */ public boolean isConnectionPool() { return _isConnectionPool; } /** * Adds a java class */ public void addJavaClass(String name, Class<?> type) throws ConfigException { try { if (type.isAnnotationPresent(ClassImplementation.class)) _moduleContext.introspectJavaImplClass(name, type, null); else _moduleContext.introspectJavaClass(name, type, null, null); } catch (Exception e) { throw ConfigException.create(e); } } /** * Adds a java class */ public void addJavaClass(String phpName, String className) { Class type; try { type = Class.forName(className, false, _loader); } catch (ClassNotFoundException e) { throw new QuercusRuntimeException(L.l("`{0}' not valid: {1}", className, e.toString()), e); } addJavaClass(phpName, type); } /** * Adds a impl class */ public void addImplClass(String name, Class type) throws ConfigException { throw new UnsupportedOperationException("XXX: need to merge with ModuleContext: " + name); /* try { introspectJavaImplClass(name, type, null); } catch (Exception e) { throw ConfigException.create(e); } */ } /** * Adds a java class */ public JavaClassDef getJavaClassDefinition(Class type, String className) { JavaClassDef def; if (_classNotFoundCache.get(className) != null) return null; def = _javaClassWrappers.get(className); if (def == null) { try { def = getModuleContext().getJavaClassDefinition(type, className); int id = getClassId(className); _classDefMap[id] = def; _javaClassWrappers.put(className, def); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new QuercusRuntimeException(e); } } def.init(); return def; } /** * Adds a java class */ public JavaClassDef getJavaClassDefinition(String className) { JavaClassDef def; if (_classNotFoundCache.get(className) != null) return null; def = _javaClassWrappers.get(className); if (def == null) { try { def = getModuleContext().getJavaClassDefinition(className); _javaClassWrappers.put(className, def); } catch (RuntimeException e) { _classNotFoundCache.put(className, className); throw e; } catch (Exception e) { throw new QuercusRuntimeException(e); } } def.init(); return def; } /** * Finds the java class wrapper. */ public ClassDef findJavaClassWrapper(String name) { ClassDef def = _javaClassWrappers.get(name); if (def != null) return def; return _lowerJavaClassWrappers.get(name.toLowerCase()); } /** * Sets an ini file. */ public void setIniFile(Path path) { // XXX: Not sure why this dependency would be useful // Environment.addDependency(new Depend(path)); if (path.canRead()) { Env env = new Env(this); Value result = FileModule.parse_ini_file(env, path, false); if (result instanceof ArrayValue) { ArrayValue array = (ArrayValue) result; for (Map.Entry<Value,Value> entry : array.entrySet()) { setIni(entry.getKey().toString(), entry.getValue().toString()); } } _iniFile = path; } } /** * Returns the ini file. */ public Path getIniFile() { return _iniFile; } /** * Returns the IniDefinitions for all ini that have been defined by modules. */ public IniDefinitions getIniDefinitions() { return _iniDefinitions; } /** * Returns a map of the ini values that have been explicitly set. */ public HashMap<String, Value> getIniMap(boolean create) { if (_iniMap == null && create) _iniMap = new HashMap<String, Value>(); return _iniMap; } /** * Sets an ini value. */ public void setIni(String name, StringValue value) { _iniDefinitions.get(name).set(this, value); } /** * Sets an ini value. */ public void setIni(String name, String value) { _iniDefinitions.get(name).set(this, value); } /** * Returns an ini value. */ public boolean getIniBoolean(String name) { return _iniDefinitions.get(name).getAsBoolean(this); } /** * Returns an ini value as a long. */ public long getIniLong(String name) { return _iniDefinitions.get(name).getAsLongValue(this).toLong(); } /** * Returns an ini value. */ public Value getIniValue(String name) { return _iniDefinitions.get(name).getValue(this); } /** * Sets a server env value. */ public void setServerEnv(String name, String value) { setServerEnv(createString(name), new ConstStringValue(value)); } /** * Sets a server env value. */ public void setServerEnv(StringValue name, StringValue value) { _serverEnvMap.put(name, value); } /** * Gets a server env value. */ public Value getServerEnv(StringValue name) { return _serverEnvMap.get(name); } /** * Returns the server env map. */ public HashMap<Value,Value> getServerEnvMap() { return _serverEnvMap; } /** * Returns an include path. */ public Path getIncludeCache(StringValue include, String includePath, Path pwd, Path scriptPwd) { IncludeKey key = new IncludeKey(include, includePath, pwd, scriptPwd); Path path = _includeCache.get(key); return path; } /** * Adds an include path. */ public void putIncludeCache(StringValue include, String includePath, Path pwd, Path scriptPwd, Path path) { IncludeKey key = new IncludeKey(include, includePath, pwd, scriptPwd); _includeCache.put(key, path); } /** * Returns the definition cache hit count. */ public long getDefCacheHitCount() { return _defCacheHitCount; } /** * Returns the definition cache miss count. */ public long getDefCacheMissCount() { return _defCacheMissCount; } /** * Clears the definition cache. */ public void clearDefinitionCache() { // _defCache.clear(); } /** * Returns true if a precompiled page exists */ public boolean includeExists(Path path) { return _pageManager.precompileExists(path); } /** * Parses a quercus program. * * @param path the source file path * @return the parsed program * @throws IOException */ public QuercusPage parse(Path path) throws IOException { return _pageManager.parse(path); } /** * Parses a quercus program. * * @param path the source file path * @return the parsed program * @throws IOException */ public QuercusPage parse(Path path, String fileName, int line) throws IOException { return _pageManager.parse(path, fileName, line); } /** * Parses a quercus program. * * @param path the source file path * @return the parsed program * @throws IOException */ public QuercusPage parse(ReadStream is) throws IOException { return new InterpretedPage(QuercusParser.parse(this, is)); } /** * Parses a quercus string. * * @param code the source code * @return the parsed program * @throws IOException */ public QuercusProgram parseCode(String code) throws IOException { QuercusProgram program = _evalCache.get(code); if (program == null) { program = QuercusParser.parseEval(this, code); _evalCache.put(code, program); } return program; } /** * Parses a quercus string. * * @param code the source code * @return the parsed program * @throws IOException */ public QuercusProgram parseEvalExpr(String code) throws IOException { // XXX: possible conflict with parse eval because of the // return value changes QuercusProgram program = _evalCache.get(code); if (program == null) { program = QuercusParser.parseEvalExpr(this, code); _evalCache.put(code, program); } return program; } /** * Parses a function. * * @param args the arguments * @param code the source code * @return the parsed program * @throws IOException */ public AbstractFunction parseFunction(String name, String args, String code) throws IOException { return QuercusParser.parseFunction(this, name, args, code); } /** * Returns the function with the given name. */ public AbstractFunction findFunction(String name) { AbstractFunction fun = _funMap.get(name); if ((fun == null) && ! isStrict()) fun = _lowerFunMap.get(name.toLowerCase()); return fun; } /** * Returns the function with the given name. */ public AbstractFunction findFunctionImpl(String name) { AbstractFunction fun = _funMap.get(name); return fun; } /** * Returns the function with the given name. */ public AbstractFunction findLowerFunctionImpl(String lowerName) { AbstractFunction fun = _lowerFunMap.get(lowerName); return fun; } /** * Returns an array of the defined functions. */ public ArrayValue getDefinedFunctions() { ArrayValue internal = new ArrayValueImpl(); for (String name : _funMap.keySet()) { internal.put(name); } return internal; } // // name to id mappings // /** * Returns the id for a function name. */ public int getFunctionId(String name) { if (! isStrict()) name = name.toLowerCase(); int id = _functionNameMap.get(name); if (id >= 0) return id; synchronized (_functionNameMap) { id = _functionNameMap.get(name); if (id >= 0) return id; id = _functionNameMap.size(); extendFunctionMap(name, id); _functionNameMap.put(name, id); } return id; } protected void extendFunctionMap(String name, int id) { if (_functionMap.length <= id) { AbstractFunction []functionMap = new AbstractFunction[id + 256]; System.arraycopy(_functionMap, 0, functionMap, 0, _functionMap.length); _functionMap = functionMap; } _functionMap[id] = new UndefinedFunction(name); } /** * Returns the id for a function name. */ public int findFunctionId(String name) { if (! isStrict()) name = name.toLowerCase(); // IntMap is internally synchronized return _functionNameMap.get(name); } /** * Returns the number of functions */ public int getFunctionIdCount() { return _functionNameMap.size(); } /** * Returns the undefined functions */ public AbstractFunction []getFunctionMap() { return _functionMap; } public int setFunction(String name, AbstractFunction fun) { int id = getFunctionId(name); _functionMap[id] = fun; return id; } /** * Returns the id for a class name. */ public int getClassId(String className) { int id = _classNameMap.get(className); if (id >= 0) return id; synchronized (_classNameMap) { String name = className.toLowerCase(); id = _classNameMap.get(name); if (id >= 0) { _classNameMap.put(className, id); return id; } id = _classNameMap.size(); if (_classDefMap.length <= id) { String []classNames = new String[id + 256]; System.arraycopy(_classNames, 0, classNames, 0, _classNames.length); _classNames = classNames; ClassDef []classDefMap = new ClassDef[_classNames.length]; System.arraycopy(_classDefMap, 0, classDefMap, 0, _classDefMap.length); _classDefMap = classDefMap; QuercusClass []classCacheMap = new QuercusClass[_classNames.length]; System.arraycopy(_classCacheMap, 0, classCacheMap, 0, _classCacheMap.length); _classCacheMap = classCacheMap; } _classNames[id] = className; _classNameMap.put(className, id); _classNameMap.put(name, id); } return id; } public String getClassName(int id) { return _classNames[id]; } /** * Returns the id for a function name. */ public int findClassId(String name) { return _classNameMap.get(name); } /** * Returns the number of classes */ public int getClassIdCount() { return _classNameMap.size(); } /** * Returns the undefined functions */ public ClassDef []getClassDefMap() { return _classDefMap; } /** * Returns the class def with the given index. */ public ClassDef getClassDef(int id) { return _classDefMap[id]; } /** * Returns the undefined functions */ public QuercusClass []getClassCacheMap() { return _classCacheMap; } /** * Returns the undefined functions */ public QuercusClass getCachedClass(int id) { return _classCacheMap[id]; } /** * Returns the undefined functions */ public void setCachedClass(int id, QuercusClass qClass) { _classCacheMap[id] = qClass; } /** * Returns the id for a constant */ public int getConstantId(String name) { return getConstantId(new ConstStringValue(name)); } /** * Returns the id for a constant */ public int getConstantId(StringValue name) { int id = _constantNameMap.get(name); if (id >= 0) return id; synchronized (_constantNameMap) { id = _constantNameMap.get(name); if (id >= 0) return id; id = _constantNameMap.size(); if (_classDefMap.length <= id) { Value []constantMap = new Value[id + 256]; System.arraycopy(_constantMap, 0, constantMap, 0, _constantMap.length); _constantMap = constantMap; Value []constantNameList = new Value[id + 256]; System.arraycopy(_constantNameList, 0, constantNameList, 0, _constantNameList.length); _constantNameList = constantNameList; int []constantLowerMap = new int[_constantMap.length]; System.arraycopy(_constantLowerMap, 0, constantLowerMap, 0, _constantLowerMap.length); _constantLowerMap = constantLowerMap; } // XXX: i18n _constantNameList[id] = name; // php/0501 int lowerId; if (! name.equals(name.toLowerCase())) lowerId = getConstantId(name.toLowerCase()); else lowerId = id; _constantLowerMap[id] = lowerId; _constantNameMap.put(name, id); } return id; } /** * Returns the name map. */ public int getConstantLower(int id) { return _constantLowerMap[id]; } /** * Returns the name map. */ public Value getConstantName(int id) { return _constantNameList[id]; } /** * Returns the name map. */ public Value []getConstantMap() { return _constantMap; } /** * Returns the number of defined constants */ public int getConstantIdCount() { return _constantNameMap.size(); } /** * Returns true if the variable is a superglobal. */ public static boolean isSuperGlobal(String name) { return _superGlobals.contains(name); } /** * Returns the stdClass definition. */ public QuercusClass getStdClass() { return _moduleContext.getStdClass(); } /** * Returns the class with the given name. */ public ClassDef findClass(String name) { int id = getClassId(name); return _classDefMap[id]; } /** * Returns the class maps. */ public HashMap<String, ClassDef> getClassMap() { throw new UnsupportedOperationException(); } /** * Returns the module with the given name. */ public QuercusModule findModule(String name) { ModuleInfo moduleInfo = _modules.get(name); QuercusModule module = null; if (moduleInfo != null) module = moduleInfo.getModule(); else module = getModuleContext().findModule(name); if (module == null) throw new IllegalStateException(L.l("'{0}' is an unknown quercus module", name)); return module; } /** * Returns a list of the modules that have some startup code to run. */ public HashSet<ModuleStartupListener> getModuleStartupListeners() { return _moduleStartupListeners; } /** * Returns true if an extension is loaded. */ public boolean isExtensionLoaded(String name) { return _extensionSet.contains(name) || _extensionSetLowerCase.contains(name.toLowerCase()); } /** * Returns the loaded extensions. */ public HashSet<String> getLoadedExtensions() { return _extensionSet; } /** * Returns true if an extension is loaded. */ public Value getExtensionFuncs(String name) { ArrayValue value = null; for (ModuleInfo moduleInfo : _modules.values()) { Set<String> extensionSet = moduleInfo.getLoadedExtensions(); if (extensionSet.contains(name)) { for (String functionName : moduleInfo.getFunctions().keySet()) { if (value == null) value = new ArrayValueImpl(); value.put(functionName); } } } if (value != null) return value; else return BooleanValue.FALSE; } public Collection<ModuleInfo> getModules() { return _modules.values(); } /** * Initialize the enging */ public void init() { initModules(); initClasses(); _workDir = getWorkDir(); _iniDefinitions.addAll(_ini); _includeCache = new TimedCache<IncludeKey, Path>(getIncludeCacheMax(), getIncludeCacheTimeout()); initLocal(); } public void addModule(QuercusModule module) { ModuleInfo info = new ModuleInfo(_moduleContext, module.getClass().getName(), module); addModuleInfo(info); } /** * Initializes from the ModuleContext */ private void initModules() { for (ModuleInfo info : _moduleContext.getModules()) { addModuleInfo(info); } } protected void addModuleInfo(ModuleInfo info) { _modules.put(info.getName(), info); if (info.getModule() instanceof ModuleStartupListener) _moduleStartupListeners.add((ModuleStartupListener)info.getModule()); for (String ext : info.getLoadedExtensions()) { _extensionSet.add(ext); _extensionSetLowerCase.add(ext.toLowerCase()); } Map<String, Value> map = info.getConstMap(); if (map != null) { for (Map.Entry<String,Value> entry : map.entrySet()) { int id = getConstantId(entry.getKey()); _constantMap[id] = entry.getValue(); } } _iniDefinitions.addAll(info.getIniDefinitions()); for (Map.Entry<String, AbstractFunction> entry : info.getFunctions().entrySet()) { String funName = entry.getKey(); AbstractFunction fun = entry.getValue(); _funMap.put(funName, fun); _lowerFunMap.put(funName.toLowerCase(), fun); setFunction(funName, fun); } } /** * Initializes from the ModuleContext */ private void initClasses() { for (Map.Entry<String,JavaClassDef> entry : _moduleContext.getWrapperMap().entrySet()) { String name = entry.getKey(); JavaClassDef def = entry.getValue(); _javaClassWrappers.put(name, def); _lowerJavaClassWrappers.put(name.toLowerCase(), def); } for (Map.Entry<String,ClassDef> entry : _moduleContext.getClassMap().entrySet()) { String name = entry.getKey(); ClassDef def = entry.getValue(); int id = getClassId(name); _classDefMap[id] = def; } } /** * Creates a string. Because these strings are typically Java * constants, they fit into a lru cache. */ public UnicodeBuilderValue createUnicodeString(String name) { UnicodeBuilderValue value = _unicodeMap.get(name); if (value == null) { value = new UnicodeBuilderValue(name); _unicodeMap.put(name, value); } return value; } /** * Creates a string. Because these strings are typically Java * constants, they fit into a lru cache. */ public StringValue createString(String name) { ConstStringValue value = _stringMap.get(name); if (value == null) { value = new ConstStringValue(name); _stringMap.put(name, value); } return value; } /** * Returns a named constant. */ public Value getConstant(int id) { return _constantMap[id]; } public String createStaticName() { return ("s" + _staticId++).intern(); } /** * Loads the session from the backing. */ public SessionArrayValue loadSession(Env env, String sessionId) { long now = System.currentTimeMillis(); SessionArrayValue session = _sessionManager.getSession(env, sessionId, now); if (session == null) session = _sessionManager.createSession(env, sessionId, now); return session; } /** * Saves the session to the backing. */ public void saveSession(Env env, SessionArrayValue session) { _sessionManager.saveSession(env, session); } /** * Removes the session from the backing. */ public void destroySession(String sessionId) { _sessionManager.removeSession(sessionId); } /** * Loads a special value */ public Object getSpecial(String key) { return _specialMap.get(key); } /** * Saves a special value */ public void setSpecial(String key, Object value) { _specialMap.put(key, value); } public static Value objectToValue(Object obj) { if (obj == null) return NullValue.NULL; else if (Byte.class.equals(obj.getClass()) || Short.class.equals(obj.getClass()) || Integer.class.equals(obj.getClass()) || Long.class.equals(obj.getClass())) { return LongValue.create(((Number) obj).longValue()); } else if (Float.class.equals(obj.getClass()) || Double.class.equals(obj.getClass())) { return DoubleValue.create(((Number) obj).doubleValue()); } else if (String.class.equals(obj.getClass())) { // XXX: i18n return new ConstStringValue((String) obj); } else { // XXX: unknown types, e.g. Character? return null; } } /** * Initialize local configuration, e.g. finding the PHP and PEAR libraries */ protected void initLocal() { StringBuilder sb = new StringBuilder("."); setIni("include_path", sb.toString()); } public void start() { } public Env createEnv(QuercusPage page, WriteStream out, HttpServletRequest request, HttpServletResponse response) { return new Env(this, page, out, request, response); } public ExprFactory createExprFactory() { return new ExprFactory(); } public void close() { _sessionManager.close(); _pageManager.close(); } public static Value exnConstructor(Env env, Value obj, String msg) { if (obj != null) { obj.putField(env, "message", new UnicodeValueImpl(msg)); } return NullValue.NULL; } static class IncludeKey { private final StringValue _include; private final String _includePath; private final Path _pwd; private final Path _scriptPwd; IncludeKey(StringValue include, String includePath, Path pwd, Path scriptPwd) { _include = include; _includePath = includePath; _pwd = pwd; _scriptPwd = scriptPwd; } public int hashCode() { int hash = 37; hash = 65537 * hash + _include.hashCode(); hash = 65537 * hash + _includePath.hashCode(); hash = 65537 * hash + _pwd.hashCode(); hash = 65537 * hash + _scriptPwd.hashCode(); return hash; } public boolean equals(Object o) { if (! (o instanceof IncludeKey)) return false; IncludeKey key = (IncludeKey) o; return (_include.equals(key._include) && _includePath.equals(key._includePath) && _pwd.equals(key._pwd) && _scriptPwd.equals(key._scriptPwd)); } } static { _superGlobals.add("GLOBALS"); _superGlobals.add("_COOKIE"); _superGlobals.add("_ENV"); _superGlobals.add("_FILES"); _superGlobals.add("_GET"); _superGlobals.add("_POST"); _superGlobals.add("_SERVER"); _superGlobals.add("_SESSION"); _superGlobals.add("_REQUEST"); /* String includePath; if (Path.isWindows()) includePath = "." + FileModule.PATH_SEPARATOR + "C:\\php5\\pear"; else includePath = "." + FileModule.PATH_SEPARATOR + "/usr/share/php" + FileModule.PATH_SEPARATOR + "/usr/share/pear"; INI_INCLUDE_PATH = _ini.add("include_path", includePath, IniDefinition.PHP_INI_ALL); */ } public static final IniDefinition INI_INCLUDE_PATH = _ini.add("include_path", ".", IniDefinition.PHP_INI_ALL); public static final IniDefinition INI_REGISTER_LONG_ARRAYS = _ini.add("register_long_arrays", true, IniDefinition.PHP_INI_PERDIR); public static final IniDefinition INI_ALWAYS_POPULATE_RAW_POST_DATA = _ini.add("always_populate_raw_post_data", false, IniDefinition.PHP_INI_PERDIR); // unicode ini public static final IniDefinition INI_UNICODE_SEMANTICS = _ini.add("unicode.semantics", false, IniDefinition.PHP_INI_SYSTEM); public static final IniDefinition INI_UNICODE_FALLBACK_ENCODING = _ini.add("unicode.fallback_encoding", "utf-8", IniDefinition.PHP_INI_ALL); public static final IniDefinition INI_UNICODE_FROM_ERROR_MODE = _ini.add("unicode.from_error_mode", "2", IniDefinition.PHP_INI_ALL); public static final IniDefinition INI_UNICODE_FROM_ERROR_SUBST_CHAR = _ini.add("unicode.from_error_subst_char", "3f", IniDefinition.PHP_INI_ALL); public static final IniDefinition INI_UNICODE_HTTP_INPUT_ENCODING = _ini.add("unicode.http_input_encoding", null, IniDefinition.PHP_INI_ALL); public static final IniDefinition INI_UNICODE_OUTPUT_ENCODING = _ini.add("unicode.output_encoding", null, IniDefinition.PHP_INI_ALL); public static final IniDefinition INI_UNICODE_RUNTIME_ENCODING = _ini.add("unicode.runtime_encoding", null, IniDefinition.PHP_INI_ALL); public static final IniDefinition INI_UNICODE_SCRIPT_ENCODING = _ini.add("unicode.script_encoding", null, IniDefinition.PHP_INI_ALL); }