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
A PHP engine implemented in 100% Java
/*
* 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);
}