Package com.caucho.quercus

Source Code of com.caucho.quercus.Quercus

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

}
TOP

Related Classes of com.caucho.quercus.Quercus

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.