
com.caucho.quercus.QuercusContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of quercus Show documentation
Show all versions of quercus Show documentation
Fork of Quercus by Caucho Technology.
The newest version!
/*
* Copyright (c) 1998-2012 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 java.io.IOException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.LockSupport;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.cache.Cache;
import javax.sql.DataSource;
import com.caucho.config.ConfigException;
import com.caucho.java.JavaCompilerUtil;
import com.caucho.quercus.annotation.ClassImplementation;
import com.caucho.quercus.env.AbstractJavaMethod;
import com.caucho.quercus.env.ArrayValue;
import com.caucho.quercus.env.ArrayValueImpl;
import com.caucho.quercus.env.BooleanValue;
import com.caucho.quercus.env.ConstStringValue;
import com.caucho.quercus.env.DoubleValue;
import com.caucho.quercus.env.Env;
import com.caucho.quercus.env.LongValue;
import com.caucho.quercus.env.NullValue;
import com.caucho.quercus.env.QuercusClass;
import com.caucho.quercus.env.SessionArrayValue;
import com.caucho.quercus.env.StringBuilderValue;
import com.caucho.quercus.env.StringValue;
import com.caucho.quercus.env.UnicodeBuilderValue;
import com.caucho.quercus.env.Value;
import com.caucho.quercus.expr.ExprFactory;
import com.caucho.quercus.function.AbstractFunction;
import com.caucho.quercus.lib.db.JavaSqlDriverWrapper;
import com.caucho.quercus.lib.db.JdbcDriverContext;
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.IniDefinition;
import com.caucho.quercus.module.IniDefinitions;
import com.caucho.quercus.module.ModuleContext;
import com.caucho.quercus.module.ModuleInfo;
import com.caucho.quercus.module.ModuleStartupListener;
import com.caucho.quercus.module.QuercusModule;
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.ClassDef;
import com.caucho.quercus.program.JavaClassDef;
import com.caucho.quercus.program.QuercusProgram;
import com.caucho.quercus.program.UndefinedFunction;
import com.caucho.quercus.servlet.api.QuercusHttpServletRequest;
import com.caucho.quercus.servlet.api.QuercusHttpServletResponse;
import com.caucho.quercus.servlet.api.QuercusServletContext;
import com.caucho.util.IntMap;
import com.caucho.util.L10N;
import com.caucho.util.LruCache;
import com.caucho.util.TimedCache;
import com.caucho.vfs.FilePath;
import com.caucho.vfs.Path;
import com.caucho.vfs.ReadStream;
import com.caucho.vfs.Vfs;
import com.caucho.vfs.WriteStream;
/**
* Facade for the PHP language.
*/
public class QuercusContext
{
private static L10N L = new L10N(QuercusContext.class);
private static final Logger log
= Logger.getLogger(QuercusContext.class.getName());
private static HashSet _superGlobals
= new HashSet();
private static IniDefinitions _ini = new IniDefinitions();
private final PageManager _pageManager;
private final ClassLoader _loader;
private final QuercusSessionManager _sessionManager;
private ModuleContext _moduleContext;
private static LruCache _unicodeMap
= new LruCache(8 * 1024);
private static LruCache _stringMap
= new LruCache(8 * 1024);
private HashMap _modules
= new HashMap();
private ArrayList _moduleInitList
= new ArrayList();
private HashSet _moduleStartupListeners
= new HashSet();
private HashSet _extensionSet
= new HashSet();
private HashSet _extensionSetLowerCase
= new HashSet();
private HashMap _funMap
= new HashMap();
private HashMap _lowerFunMap
= new HashMap();
private ConcurrentHashMap _javaClassWrappers
= new ConcurrentHashMap();
private LruCache _classNotFoundCache
= new LruCache(64);
private HashMap _lowerJavaClassWrappers
= new HashMap();
private HashMap> _javaInitClassMap
= new HashMap>();
private final IniDefinitions _iniDefinitions = new IniDefinitions();
private Path _iniFile;
private HashMap _iniMap;
private HashMap _serverEnvMap
= new HashMap();
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 _evalCache
= new LruCache(4096);
private int _includeCacheMax = 8192;
private long _includeCacheTimeout = 10000L;
private TimedCache _includeCache;
//private LruCache> _defCache
// = new LruCache>(4096);
private long _defCacheHitCount;
private long _defCacheMissCount;
// XXX: needs to be a timed LRU
//private LruCache _sessionMap
// = new LruCache(4096);
private ConcurrentHashMap _specialMap
= new ConcurrentHashMap();
private String _scriptEncoding;
private String _phpVersion = "5.4.0";
private String _mySqlVersion;
private boolean _isStrict;
private boolean _isLooseParse;
private boolean _isRequireSource;
private boolean _isConnectionPool = true;
private DataSource _database;
private ConcurrentHashMap _databaseMap
= new ConcurrentHashMap();
private ConcurrentHashMap _activeEnvSet
= new ConcurrentHashMap();
private long _staticId;
private Path _pwd;
private Path _workDir;
private Path _webInfDir;
private QuercusServletContext _servletContext;
private QuercusTimer _quercusTimer;
private EnvTimeoutThread _envTimeoutThread;
protected long _envTimeout = 60000L;
// how long to sleep the env timeout thread,
// for fast, complete tomcat undeploys
protected static final long ENV_TIMEOUT_UPDATE_INTERVAL = 1000L;
private long _dependencyCheckInterval = 2000L;
private boolean _isClosed;
private JdbcDriverContext _jdbcDriverContext;
private Boolean _isUnicodeSemantics;
/**
* Constructor.
*/
public QuercusContext()
{
_loader = Thread.currentThread().getContextClassLoader();
_pageManager = createPageManager();
_sessionManager = createSessionManager();
_moduleContext = getLocalContext();
}
/**
* Returns the current time.
*/
public long getCurrentTime()
{
return _quercusTimer.getCurrentTime();
}
/**
* Returns the current time in nanoseconds.
*/
public long getExactTimeNanoseconds()
{
return _quercusTimer.getExactTimeNanoseconds();
}
/**
* Returns the exact current time in milliseconds.
*/
public long getExactTime()
{
return _quercusTimer.getExactTime();
}
/**
* Returns the working directory.
*/
public Path getPwd()
{
if (_pwd == null) {
_pwd = Vfs.getPwd();
}
return _pwd;
}
/**
* Sets the working directory.
*/
public void setPwd(Path path)
{
_pwd = path;
}
public Path getWebInfDir()
{
if (_webInfDir == null) {
_webInfDir = getPwd().lookup("WEB-INF");
}
return _webInfDir;
}
public void setWebInfDir(Path path)
{
_webInfDir = path;
}
public Path getWorkDir()
{
if (_workDir == null)
_workDir = getWebInfDir().lookup("work");
return _workDir;
}
public void setWorkDir(Path workDir)
{
_workDir = workDir;
}
public String getCookieName()
{
return "JSESSIONID";
}
public final long getDependencyCheckInterval()
{
return _dependencyCheckInterval;
}
public final void setDependencyCheckInterval(long ms)
{
_dependencyCheckInterval = ms;
}
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 getName()
{
if (isPro()) {
return "Quercus Pro";
}
else {
return "Quercus";
}
}
public String getVersion()
{
return "Open Source " + QuercusVersion.getVersionNumber();
}
public String getVersionDate()
{
return QuercusVersion.getVersionDate();
}
/**
* Returns the SAPI (Server API) name.
*/
public String getSapiName()
{
// default to cgi for mediawiki-1.19.1
return "cgi";
}
public boolean isRegisterArgv() {
return getIniBoolean("register_argc_argv");
}
public boolean isProfile()
{
return false;
}
public int getProfileIndex(String name)
{
return -1;
}
public void setProfileProbability(double probability)
{
}
protected PageManager createPageManager()
{
return new PageManager(this);
}
protected QuercusSessionManager createSessionManager()
{
return new QuercusSessionManager(this);
}
/**
* Returns the context for this class loader.
*/
public final ModuleContext getLocalContext()
{
return getLocalContext(_loader);
}
public ModuleContext getLocalContext(ClassLoader loader)
{
synchronized (this) {
if (_moduleContext == null) {
_moduleContext = createModuleContext(null, loader);
_moduleContext.init();
}
}
return _moduleContext;
}
protected ModuleContext createModuleContext(ModuleContext parent,
ClassLoader loader)
{
return new ModuleContext(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;
}
public void setUnicodeSemantics(boolean isUnicode)
{
_isUnicodeSemantics = Boolean.valueOf(isUnicode);
}
/**
* Returns true if unicode.semantics is on.
*/
public boolean isUnicodeSemantics()
{
if (_isUnicodeSemantics == null) {
return false;
}
else {
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 "utf-8";
}
/**
* Sets the expected encoding of php scripts.
*/
public void setScriptEncoding(String encoding)
{
_scriptEncoding = encoding;
}
/**
* Returns the encoding used for output, null if unicode.semantics is off.
*/
public String getOutputEncoding()
{
if (! _isUnicodeSemantics)
return null;
String encoding = QuercusContext.INI_UNICODE_OUTPUT_ENCODING.getAsString(this);
if (encoding == null)
encoding = QuercusContext.INI_UNICODE_FALLBACK_ENCODING.getAsString(this);
if (encoding == null)
encoding = "utf-8";
return 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;
}
/**
* Sets the ServletContext.
*/
public void setServletContext(QuercusServletContext servletContext)
{
_servletContext = servletContext;
}
/**
* Returns the ServletContext.
*/
public QuercusServletContext 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;
}
public JdbcDriverContext getJdbcDriverContext()
{
if (_jdbcDriverContext == null) {
_jdbcDriverContext = new JdbcDriverContext();
Value driverValue = getIniValue("quercus.jdbc_drivers");
if (driverValue.isArray()) {
ArrayValue array = driverValue.toArray();
for (Map.Entry entry : array.entrySet()) {
Value key = entry.getKey();
Value value = entry.getValue();
_jdbcDriverContext.setProtocol(key.toString(), value.toString());
}
}
}
return _jdbcDriverContext;
}
/**
* 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;
}
/**
* Sets the loose mode.
*/
public void setLooseParse(boolean isLoose)
{
_isLooseParse = isLoose;
}
/**
* Gets the loose mode.
*/
public boolean isLooseParse()
{
return _isLooseParse;
}
/**
* 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;
}
private void initJavaClasses()
{
for (Map.Entry> entry : _javaInitClassMap.entrySet()) {
String name = entry.getKey();
Class cls = entry.getValue();
try {
if (cls.isAnnotationPresent(ClassImplementation.class))
_moduleContext.introspectJavaImplClass(name, cls, null);
else
_moduleContext.introspectJavaClass(name, cls, null, null);
}
catch (Exception e) {
throw new QuercusRuntimeException(e);
}
}
}
/**
* Adds a java class
*/
public void addJavaClass(String name, Class type)
throws ConfigException
{
_javaInitClassMap.put(name, type);
}
/**
* Adds a java class
*/
public void addJavaClass(String phpName, String className)
{
try {
Class type = Class.forName(className, false, _loader);
addJavaClass(phpName, type);
}
catch (ClassNotFoundException e) {
throw new QuercusRuntimeException(L.l("`{0}' not valid: {1}",
className,
e.toString()), e);
}
}
/**
* 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(Locale.ENGLISH));
}
/**
* Sets an ini file.
*/
public void setIniFile(Path path)
{
if (path != null && path.canRead()) {
_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 getIniMap(boolean create)
{
if (_iniMap == null && create)
_iniMap = new HashMap();
return _iniMap;
}
/**
* Sets an ini value.
*/
public void setIni(String name, Value 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);
}
/**
* Returns an ini value.
*/
public String getIniString(String name)
{
return _iniDefinitions.get(name).getValue(this).toJavaString();
}
/**
* Sets a server env value.
*/
public void setServerEnv(String name, String value)
{
// php/3j58
if (isUnicodeSemantics())
setServerEnv(createUnicodeString(name), createUnicodeString(value));
else
setServerEnv(createString(name), createString(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 getServerEnvMap()
{
return _serverEnvMap;
}
/**
* Returns the compile classloader
*/
public ClassLoader getCompileClassLoader()
{
return null;
}
/**
* Sets the compile classloader
*/
public void setCompileClassLoader(ClassLoader loader)
{
}
/**
* Returns the relative path.
*/
public final String getClassName(Path path)
{
if (path == null)
return "tmp.eval";
String pathName = path.getFullPath();
String pwdName = getPwd().getFullPath();
String relPath;
if (pathName.startsWith(pwdName))
relPath = pathName.substring(pwdName.length());
else
relPath = pathName;
return "_quercus." + JavaCompilerUtil.mangleName(relPath);
}
/**
* 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;
}
/**
* Returns the definition state for an include.
*/
/*
public DefinitionState getDefinitionCache(DefinitionKey key)
{
SoftReference defStateRef = _defCache.get(key);
if (defStateRef != null) {
DefinitionState defState = defStateRef.get();
if (defState != null) {
_defCacheHitCount++;
return defState.copyLazy();
}
}
_defCacheMissCount++;
return null;
}
*/
/**
* Returns the definition state for an include.
*/
/*
public void putDefinitionCache(DefinitionKey key,
DefinitionState defState)
{
_defCache.put(key, new SoftReference(defState.copy()));
}
*/
/**
* 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(StringValue 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(StringValue 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(StringValue name)
{
AbstractFunction fun = _funMap.get(name);
if ((fun == null) && ! isStrict()) {
fun = _lowerFunMap.get(name.toLowerCase(Locale.ENGLISH));
}
return fun;
}
/**
* Returns the function with the given name.
*/
public AbstractFunction findFunctionImpl(StringValue name)
{
AbstractFunction fun = _funMap.get(name);
return fun;
}
/**
* Returns the function with the given name.
*/
public AbstractFunction findLowerFunctionImpl(StringValue lowerName)
{
AbstractFunction fun = _lowerFunMap.get(lowerName);
return fun;
}
/**
* Returns an array of the defined functions.
*/
public ArrayValue getDefinedFunctions()
{
ArrayValue internal = new ArrayValueImpl();
for (StringValue name : _funMap.keySet()) {
internal.put(name);
}
return internal;
}
//
// name to id mappings
//
/**
* Returns the id for a function name.
*/
public int getFunctionId(StringValue name)
{
int id = _functionNameMap.get(name);
if (id >= 0) {
return id;
}
if (! isStrict()) {
name = name.toLowerCase(Locale.ENGLISH);
}
if (name.startsWith("\\")) {
// php/0m18
name = name.substring(1);
}
id = _functionNameMap.get(name);
if (id >= 0) {
return id;
}
synchronized (_functionNameMap) {
id = _functionNameMap.get(name);
if (id >= 0) {
return id;
}
// 0 is used for an undefined function
// php/1p0g
id = _functionNameMap.size() + 1;
_functionNameMap.put(name, id);
extendFunctionMap(name, id);
}
return id;
}
protected void extendFunctionMap(StringValue name, int id)
{
if (_functionMap.length <= id) {
AbstractFunction []functionMap = new AbstractFunction[id + 256];
System.arraycopy(_functionMap, 0,
functionMap, 0, _functionMap.length);
_functionMap = functionMap;
}
int globalId = -1;
int ns = name.lastIndexOf('\\');
if (ns > 0) {
globalId = getFunctionId(name.substring(ns + 1));
}
_functionMap[id] = new UndefinedFunction(id, name.toString(), globalId);
}
/**
* Returns the id for a function name.
*/
public int findFunctionId(StringValue name)
{
int id = _functionNameMap.get(name);
if (id >= 0) {
return id;
}
if (! isStrict()) {
name = name.toLowerCase(Locale.ENGLISH);
}
if (name.startsWith("\\")) {
name = name.substring(1);
}
id = _functionNameMap.get(name);
// IntMap is internally synchronized
return id;
}
/**
* Returns the number of functions
*/
public int getFunctionIdCount()
{
return _functionNameMap.size();
}
/**
* Returns the undefined functions
*/
public AbstractFunction []getFunctionMap()
{
return _functionMap;
}
public int setFunction(StringValue 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;
}
if (className.startsWith("\\")) {
className = className.substring(1);
}
id = _classNameMap.get(className);
if (id >= 0) {
return id;
}
synchronized (_classNameMap) {
String name = className.toLowerCase(Locale.ENGLISH);
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;
// _classMap[id] = new UndefinedClass(name);
_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)
{
// php/3j12
if (isUnicodeSemantics())
return getConstantId(new UnicodeBuilderValue(name));
else
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;
// php/313j
id = _constantNameMap.size() + 1;
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/1a0g, php/1d06
_constantNameMap.put(name, id);
// php/050a - only case-insensitive constants should add lower case,
// i.e. use addLowerConstantId
}
return id;
}
/**
* Returns the id for a constant
*/
public int addLowerConstantId(StringValue name)
{
int id = getConstantId(name);
int lowerId = getConstantId(name.toLowerCase());
_constantLowerMap[id] = lowerId;
return id;
}
/**
* Returns the name map.
*/
public int getConstantLower(int id)
{
return _constantLowerMap[id];
}
/**
* Returns the constant id.
*/
public int getConstantLowerId(String name)
{
return getConstantId(name.toLowerCase(Locale.ENGLISH));
}
/**
* 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(StringValue name)
{
return _superGlobals.contains(name.toString());
}
/**
* 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 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 getModuleStartupListeners()
{
return _moduleStartupListeners;
}
/**
* Returns true if an extension is loaded.
*/
public boolean isExtensionLoaded(String name)
{
return _extensionSet.contains(name)
|| _extensionSetLowerCase.contains(name.toLowerCase(Locale.ENGLISH));
}
/**
* Returns the loaded extensions.
*/
public HashSet getLoadedExtensions()
{
return _extensionSet;
}
/**
* Returns true if an extension is loaded.
*/
public Value getExtensionFuncs(String name)
{
ArrayValue value = null;
for (ModuleInfo moduleInfo : _modules.values()) {
Set 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 getModules()
{
return _modules.values();
}
/**
* Initialize the engine
*/
public void init()
{
initLocal();
initModules();
initClasses();
if (_iniFile != null) {
Env env = new Env(this);
Value result = FileModule.parse_ini_file(env, _iniFile, false);
if (result instanceof ArrayValue) {
ArrayValue array = (ArrayValue) result;
for (Map.Entry entry : array.entrySet()) {
String key = entry.getKey().toString();
Value value = entry.getValue();
setIni(key, value);
}
}
}
if (_isUnicodeSemantics == null) {
_isUnicodeSemantics = getIniBoolean("unicode.semantics");
}
_moduleContext.setUnicodeSemantics(_isUnicodeSemantics);
for (Map.Entry entry : System.getenv().entrySet()) {
_serverEnvMap.put(createString(entry.getKey()),
createString(entry.getValue()));
}
_workDir = getWorkDir();
_iniDefinitions.addAll(_ini);
_includeCache = new TimedCache(getIncludeCacheMax(),
getIncludeCacheTimeout());
}
public void addInitModule(QuercusModule module)
{
ModuleInfo info = new ModuleInfo(module.getClass().getName(),
module);
_moduleInitList.add(info);
}
/**
* Initializes from the ModuleContext
*/
private void initModules()
{
HashSet disableSet = null;
String value = getIniString("disable_functions");
if (value != null) {
disableSet = new HashSet();
String[] values = value.split(",");
for (String name : values) {
disableSet.add(name.trim());
}
}
for (ModuleInfo info : _moduleInitList) {
initModuleInfo(info, disableSet);
}
for (ModuleInfo info : _moduleContext.getModules()) {
initModuleInfo(info, disableSet);
}
}
private void initModuleInfo(ModuleInfo info, HashSet disableSet)
{
_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(Locale.ENGLISH));
}
Map map;
if (isUnicodeSemantics())
map = info.getUnicodeConstMap();
else
map = info.getConstMap();
if (map != null) {
for (Map.Entry entry : map.entrySet()) {
int id = getConstantId(entry.getKey());
_constantMap[id] = entry.getValue();
}
}
_iniDefinitions.addAll(info.getIniDefinitions());
for (Map.Entry entry
: info.getFunctions().entrySet()) {
String funName = entry.getKey();
Method[] methods = entry.getValue();
if (disableSet != null && disableSet.contains(funName)) {
continue;
}
AbstractJavaMethod fun
= _moduleContext.createStaticFunction(info.getModule(), methods[0]);
for (int i = 1; i < methods.length; i++) {
AbstractJavaMethod overload
= _moduleContext.createStaticFunction(info.getModule(), methods[i]);
fun = fun.overload(overload);
}
StringValue funNameV = createString(funName);
_funMap.put(funNameV, fun);
_lowerFunMap.put(funNameV.toLowerCase(Locale.ENGLISH), fun);
setFunction(funNameV, fun);
}
}
/**
* Initializes from the ModuleContext
*/
private void initClasses()
{
initJavaClasses();
ModuleContext context = _moduleContext;
for (Map.Entry entry : context.getWrapperMap().entrySet()) {
String name = entry.getKey();
JavaClassDef def = entry.getValue();
_javaClassWrappers.put(name, def);
_lowerJavaClassWrappers.put(name.toLowerCase(Locale.ENGLISH), def);
}
for (Map.Entry entry : context.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)
{
if (isUnicodeSemantics()) {
UnicodeBuilderValue value = _unicodeMap.get(name);
if (value == null) {
if (isUnicodeSemantics()) {
value = new UnicodeBuilderValue(name);
}
_unicodeMap.put(name, value);
}
return value;
}
else {
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 StringValue createStaticName()
{
StringValue str = createString("s" + _staticId++);
return str;
}
public Cache getSessionCache()
{
return null;
}
public void setSessionTimeout(long sessionTimeout)
{
}
/**
* Loads the session from the backing.
*/
public SessionArrayValue loadSession(Env env, String sessionId)
{
long now = env.getCurrentTime();
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(".");
Path pwd = getPwd();
String []paths = new String[] {
"/usr/share/php", "/usr/lib/php", "/usr/local/lib/php",
"/usr/share/pear", "/usr/lib/pear", "/usr/local/lib/pear"
};
for (String path : paths) {
if (pwd.lookup(path).isDirectory()) {
sb.append(":").append(pwd.lookup(path).getPath());
}
}
setIni("include_path", sb.toString());
}
public void start()
{
try {
_quercusTimer = new QuercusTimer();
_envTimeoutThread = new EnvTimeoutThread();
_envTimeoutThread.start();
} catch (Exception e) {
log.log(Level.FINE, e.getMessage(), e);
}
}
public Env createEnv(QuercusPage page,
WriteStream out,
QuercusHttpServletRequest request,
QuercusHttpServletResponse response)
{
return new Env(this, page, out, request, response);
}
public ExprFactory createExprFactory()
{
return new ExprFactory();
}
protected Map getActiveEnvSet()
{
return _activeEnvSet;
}
public void startEnv(Env env)
{
_activeEnvSet.put(env, env);
}
public void completeEnv(Env env)
{
_activeEnvSet.remove(env);
}
protected boolean isClosed()
{
return _isClosed;
}
public void close()
{
synchronized (this) {
if (_isClosed) {
return;
}
_isClosed = true;
}
_sessionManager.close();
_pageManager.close();
EnvTimeoutThread envTimeoutThread = _envTimeoutThread;
_envTimeoutThread = null;
if (envTimeoutThread != null) {
envTimeoutThread.shutdown();
}
QuercusTimer quercusTimer = _quercusTimer;
_quercusTimer = null;
if (quercusTimer != null) {
quercusTimer.shutdown();
}
}
/**
* Calls close().
*/
@Override
protected void finalize()
throws Throwable
{
super.finalize();
close();
}
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();
if (_pwd != null)
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));
}
}
class EnvTimeoutThread extends Thread {
private volatile boolean _isRunnable = true;
private final long _timeout = _envTimeout;
private long _quantumCount;
EnvTimeoutThread()
{
super("quercus-env-timeout");
setDaemon(true);
//setPriority(Thread.MAX_PRIORITY);
}
public void shutdown()
{
_isRunnable = false;
LockSupport.unpark(this);
try {
Thread.sleep(ENV_TIMEOUT_UPDATE_INTERVAL * 2);
}
catch (InterruptedException e) {
}
}
public void run()
{
while (_isRunnable) {
if (_quantumCount >= _timeout) {
_quantumCount = 0;
try {
ArrayList activeEnv
= new ArrayList(_activeEnvSet.keySet());
for (Env env : activeEnv) {
env.updateTimeout();
}
} catch (Throwable e) {
}
}
else {
_quantumCount += ENV_TIMEOUT_UPDATE_INTERVAL;
}
LockSupport.parkNanos(ENV_TIMEOUT_UPDATE_INTERVAL * 1000000L);
}
}
}
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);
}