Package com.caucho.quercus.env

Source Code of com.caucho.quercus.env.Env$FieldGetEntry

/*
* 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.env;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.Set;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;

import com.caucho.quercus.Location;
import com.caucho.quercus.Quercus;
import com.caucho.quercus.QuercusDieException;
import com.caucho.quercus.QuercusErrorException;
import com.caucho.quercus.QuercusException;
import com.caucho.quercus.QuercusExitException;
import com.caucho.quercus.QuercusModuleException;
import com.caucho.quercus.QuercusRuntimeException;
import com.caucho.quercus.expr.Expr;
import com.caucho.quercus.function.AbstractFunction;
import com.caucho.quercus.lib.ErrorModule;
import com.caucho.quercus.lib.OptionsModule;
import com.caucho.quercus.lib.VariableModule;
import com.caucho.quercus.lib.file.FileModule;
import com.caucho.quercus.lib.file.PhpStderr;
import com.caucho.quercus.lib.file.PhpStdin;
import com.caucho.quercus.lib.file.PhpStdout;
import com.caucho.quercus.lib.regexp.RegexpState;
import com.caucho.quercus.lib.string.StringModule;
import com.caucho.quercus.lib.string.StringUtility;
import com.caucho.quercus.module.IniDefinition;
import com.caucho.quercus.module.ModuleContext;
import com.caucho.quercus.module.ModuleStartupListener;
import com.caucho.quercus.page.QuercusPage;
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.resources.StreamContextResource;
import com.caucho.util.CharBuffer;
import com.caucho.util.FreeList;
import com.caucho.util.IntMap;
import com.caucho.util.L10N;
import com.caucho.util.QDate;
import com.caucho.vfs.ByteToChar;
import com.caucho.vfs.Encoding;
import com.caucho.vfs.MemoryPath;
import com.caucho.vfs.NullPath;
import com.caucho.vfs.Path;
import com.caucho.vfs.ReadStream;
import com.caucho.vfs.TempBuffer;
import com.caucho.vfs.WriteStream;
import com.caucho.vfs.i18n.EncodingReader;

/**
* Represents the Quercus environment.
*/
public class Env {
  private static final L10N L = new L10N(Env.class);
  private static final Logger log
    = Logger.getLogger(Env.class.getName());

  public static final int B_ERROR = 0;
  public static final int B_WARNING = 1;
  public static final int B_PARSE = 2;
  public static final int B_NOTICE = 3;
  public static final int B_CORE_ERROR = 4;
  public static final int B_CORE_WARNING = 5;
  public static final int B_COMPILE_ERROR = 6;
  public static final int B_COMPILE_WARNING = 7;
  public static final int B_USER_ERROR = 8;
  public static final int B_USER_WARNING = 9;
  public static final int B_USER_NOTICE = 10;
  public static final int B_STRICT = 11;
  public static final int B_RECOVERABLE_ERROR = 12;
 
  public static final int B_LAST = B_RECOVERABLE_ERROR;

  public static final int E_ERROR = 1 << B_ERROR;
  public static final int E_WARNING = 1 << B_WARNING;
  public static final int E_PARSE = 1 << B_PARSE;
  public static final int E_NOTICE = 1 << B_NOTICE;
  public static final int E_CORE_ERROR = 1 << B_CORE_ERROR;
  public static final int E_CORE_WARNING = 1 << B_CORE_WARNING;
  public static final int E_COMPILE_ERROR = 1 << B_COMPILE_ERROR;
  public static final int E_COMPILE_WARNING = 1 << B_COMPILE_WARNING;
  public static final int E_USER_ERROR = 1 << B_USER_ERROR;
  public static final int E_USER_WARNING = 1 << B_USER_WARNING;
  public static final int E_USER_NOTICE = 1 << B_USER_NOTICE;
  public static final int E_ALL = 6143; //(4096 + 2048 - 1)
  public static final int E_STRICT = 1 << B_STRICT;
  public static final int E_RECOVERABLE_ERROR = 1 << B_RECOVERABLE_ERROR;

  public static final int E_DEFAULT = E_ALL & ~E_NOTICE;

  private static final int _SERVER = 1;
  private static final int _GET = 2;
  private static final int _POST = 3;
  private static final int _COOKIE = 4;
  private static final int _GLOBAL = 5;
  private static final int _REQUEST = 6;
  private static final int _SESSION = 7;
  private static final int HTTP_GET_VARS = 8;
  private static final int HTTP_POST_VARS = 9;
  private static final int HTTP_COOKIE_VARS = 10;
  private static final int PHP_SELF = 11;
  private static final int _FILES = 12;
  private static final int HTTP_POST_FILES = 13;
  private static final int _ENV = 14;
  private static final int HTTP_SERVER_VARS = 15;
  private static final int HTTP_RAW_POST_DATA = 16;

  private static final IntMap SPECIAL_VARS = new IntMap();

  private static final StringValue PHP_SELF_STRING
    = new ConstStringValue("PHP_SELF");

  private static final StringValue UTF8_STRING
    = new ConstStringValue("utf-8");

  public static final Value []EMPTY_VALUE = new Value[0];

  private static ThreadLocal<Env> _threadEnv = new ThreadLocal<Env>();

  private static final FreeList<AbstractFunction[]> _freeFunList
    = new FreeList<AbstractFunction[]>(256);

  private static final FreeList<ClassDef[]> _freeClassDefList
    = new FreeList<ClassDef[]>(256);

  private static final FreeList<QuercusClass[]> _freeClassList
    = new FreeList<QuercusClass[]>(256);

  private static final FreeList<Value[]> _freeConstList
    = new FreeList<Value[]>(256);

  private static final FreeList<QDate> _freeGmtDateList
    = new FreeList<QDate>(256);

  private static final FreeList<QDate> _freeLocalDateList
    = new FreeList<QDate>(256);

  protected final Quercus _quercus;
 
  private QuercusPage _page;

  private HashMap<String,Value> _scriptGlobalMap
    = new HashMap<String,Value>(16);

  // Function map
  public AbstractFunction []_fun;
 
  // anonymous functions created by create_function()
  public HashMap<String, AbstractFunction> _anonymousFunMap;
 
  // Class map
  public ClassDef []_classDef;
  public QuercusClass []_qClass;

  // Constant map
  public Value []_const;

  // Globals
  private Map<String, EnvVar> _globalMap
    = new HashMap<String, EnvVar>();

  private EnvVar []_globalList;
 
  // Current env
  private Map<String, EnvVar> _map = _globalMap;

  private HashMap<String, Value> _iniMap;

  // specialMap is used for implicit resources like the mysql link
  private HashMap<String, Object> _specialMap
    = new HashMap<String, Object>();

  private HashSet<String> _initializedClassSet
    = new HashSet<String>();

  // include_path ini
  private int _iniCount = 1;

  private ArrayList<EnvCleanup> _cleanupList;
  private ArrayList<ObjectExtValue> _objCleanupList;
  private ArrayList<Shutdown> _shutdownList;

  private String _defaultIncludePath;
  private String _includePath;
  private int _includePathIniCount;
  private ArrayList<String> _includePathList;
  private HashMap<Path,ArrayList<Path>> _includePathMap;

  // XXX: may require a LinkedHashMap for ordering?
  private HashMap<Path,QuercusPage> _includeMap
    = new HashMap<Path,QuercusPage>();
 
  private Value _this = NullThisValue.NULL;

  private final boolean _isUnicodeSemantics;
  private boolean _isAllowUrlInclude;
  private boolean _isAllowUrlFopen;
 
  private HashMap<StringValue,Path> _lookupCache
    = new HashMap<StringValue,Path>();

  private HashMap<ConnectionEntry,ConnectionEntry> _connMap
    = new HashMap<ConnectionEntry,ConnectionEntry>();

  private AbstractFunction _autoload;
  private HashSet<String> _autoloadClasses
    = new HashSet<String>();
 
  private ArrayList<Callback> _autoloadList;
  private InternalAutoloadCallback _internalAutoload;

  private long _startTime;
  private long _timeLimit = 600000L;
  private long _endTime;

  private Expr [] _callStack;
  private Value [] _callThisStack;
  private Value [][] _callArgStack;
  private int _callStackTop;
 
  private QuercusClass _callingClass;

  private Value [] _functionArgs;
 
  private LinkedList<FieldGetEntry> _fieldGetList
    = new LinkedList<FieldGetEntry>();

  private Path _selfPath;
  private Path _selfDirectory;
  private Path _pwd;
  private Path _uploadPath;
  private Path _tmpPath;
  private ArrayList<Path> _removePaths;

  private final boolean _isStrict;

  private HttpServletRequest _request;
  private HttpServletResponse _response;

  private ArrayValue _postArray;
  private StringValue _inputData;
 
  private ArrayValue _files;
  private SessionArrayValue _session;
  private HttpSession _javaSession;

  private ScriptContext _scriptContext;

  private WriteStream _originalOut;
  private OutputBuffer _outputBuffer;

  private WriteStream _out;

  private LocaleInfo _locale;

  private Callback [] _prevErrorHandlers = new Callback[B_LAST + 1];
  private Callback [] _errorHandlers = new Callback[B_LAST + 1];

  private Callback _prevExceptionHandler;
  private Callback _exceptionHandler;

  private SessionCallback _sessionCallback;

  private StreamContextResource _defaultStreamContext;

  // XXX: need to look this up from the module itself
  private int _errorMask = E_DEFAULT;

  private int _objectId = 0;

  private Logger _logger;

  // hold special Quercus php import statements
  private ImportMap _importMap;

  private TimeZone _defaultTimeZone;
  private QDate _localDate;
  private QDate _gmtDate;

  private Object _gzStream;

  private Env _oldThreadEnv;

  private RegexpState _freeRegexpState;
 
  private CharBuffer _cb = new CharBuffer();
 
  public Env(Quercus quercus,
             QuercusPage page,
             WriteStream out,
             HttpServletRequest request,
             HttpServletResponse response)
  {
    _quercus = quercus;

    _isStrict = quercus.isStrict();
    _isUnicodeSemantics = quercus.isUnicodeSemantics();
   
    _isAllowUrlInclude = quercus.isAllowUrlInclude();
    _isAllowUrlFopen = quercus.isAllowUrlFopen();

    _page = page;

    // XXX: grab initial from page
    // _defState = new DefinitionState(quercus);

    AbstractFunction []defFuns = getDefaultFunctionMap();
    _fun = _freeFunList.allocate();
    if (_fun == null || _fun.length < defFuns.length)
      _fun = new AbstractFunction[defFuns.length];
    System.arraycopy(defFuns, 0, _fun, 0, defFuns.length);

    ClassDef []defClasses = quercus.getClassDefMap();

    _classDef = _freeClassDefList.allocate();
    if (_classDef == null || _classDef.length < defClasses.length)
      _classDef = new ClassDef[defClasses.length];
    else {
      // list should have been zeroed on call to free
    }

    _qClass = _freeClassList.allocate();
    if (_qClass == null || _qClass.length < defClasses.length)
      _qClass = new QuercusClass[defClasses.length];
    else {
      // list should have been zeroed on call to free
    }
   
    Value []defConst = quercus.getConstantMap();
   
    _const = _freeConstList.allocate();
    if (_const == null || _const.length < defConst.length)
      _const = new Value[defConst.length];
    else {
      // list should have been zeroed on call to free
    }
   
    System.arraycopy(defConst, 0, _const, 0, defConst.length);
   
    _originalOut = out;
    _out = out;

    _request = request;
    _response = response;

    if (_page != null) {
      pageInit(_page);
    }

    setPwd(_quercus.getPwd());

    if (_page != null) {
      setSelfPath(_page.getSelfPath(null));

      // php/0b32
      _includeMap.put(_selfPath, _page);
    }

    _internalAutoload
      = new InternalAutoloadCallback("com/caucho/quercus/php/");
   
    if (_request != null && _request.getMethod().equals("POST")) {
      _postArray = new ArrayValueImpl();
      _files = new ArrayValueImpl();
      Post.fillPost(this,
                    _postArray,
                    _files,
                    _request,
                    getIniBoolean("magic_quotes_gpc"));
    } else if (_request != null && ! _request.getMethod().equals("GET")) {
      InputStream is = null;
     
      try {
        is = _request.getInputStream();
      } catch (IOException e) {
        warning(e);
      }
     
      StringValue bb = createBinaryBuilder();
      bb.appendReadAll(is, Integer.MAX_VALUE);
     
      setInputData(bb);
    }

    // Define the constant string PHP_VERSION

    addConstant("PHP_VERSION", OptionsModule.phpversion(this, null), true);
   
    // STDIN, STDOUT, STDERR
    // php://stdin, php://stdout, php://stderr
    if (response == null) {
      addConstant("STDOUT", wrapJava(new PhpStdout()), false);
      addConstant("STDERR", wrapJava(new PhpStderr()), false);
      addConstant("STDIN", wrapJava(new PhpStdin(this)), false);
    }
  }

  public Env(Quercus quercus)
  {
    this(quercus, null, null, null, null);
  }

  public static Env getCurrent()
  {
    return  _threadEnv.get();
  }

  public static Env getInstance()
  {
    return getCurrent();
  }

  protected AbstractFunction []getDefaultFunctionMap()
  {
    return getQuercus().getFunctionMap();
  }

  /**
   * Initialize the page, loading any functions and classes
   */
  protected QuercusPage pageInit(QuercusPage page)
  {
    if (page.getCompiledPage() != null)
      page = page.getCompiledPage();
   
    page.init(this);

    page.importDefinitions(this);

    return page;
  }
 
  //
  // script accessible methods
  //

  /**
   * External calls to set a global value.
   */
  public Value setScriptGlobal(String name, Object object)
  {
    Value value;

    if (object instanceof Value)
      value = (Value) object;
    else if (object == null)
      value = NullValue.NULL;
    else
      value = wrapJava(object);
   
    _scriptGlobalMap.put(name, value);

    return value;
  }

  //
  // i18n
  //

  /**
   * Returns true if unicode.semantics is on.
   */
  public boolean isUnicodeSemantics()
  {
    return _isUnicodeSemantics;
  }

  /**
   * Returns the encoding used for scripts.
   */
  public String getScriptEncoding()
  {
    StringValue encoding = getIni("unicode.script_encoding");

    if (encoding.length() == 0) {
      encoding = getIni("unicode.fallback_encoding");

      if (encoding.length() == 0)
        return getQuercus().getScriptEncoding();
    }

    return encoding.toString();
  }

  /**
   * Returns the encoding used for runtime conversions, e.g. files
   * XXX: ISO-8859-1 when unicode.semantics is OFF
   */
  public String getRuntimeEncoding()
  {
    if (! _isUnicodeSemantics)
      return "iso-8859-1";
   
    StringValue encoding = getIni("unicode.runtime_encoding");

    if (encoding.length() == 0) {
      encoding = getIni("unicode.fallback_encoding");

      if (encoding.length() == 0)
        encoding = UTF8_STRING;
    }

    return encoding.toString();
  }

  /**
   * Sets the encoding used for runtime conversions.
   */
  public Value setRuntimeEncoding(String encoding)
  {
    return setIni("unicode.runtime_encoding", encoding);
  }

  /**
   * Returns the encoding used for runtime conversions, e.g. files
   */
  public EncodingReader getRuntimeEncodingFactory()
    throws IOException
  {
    return Encoding.getReadFactory(getRuntimeEncoding());
  }

  /**
   * Returns the encoding used for input, i.e. post,
   * null if unicode.semantics is off.
   */
  public String getHttpInputEncoding()
  {
    if (! _isUnicodeSemantics)
      return getScriptEncoding();
   
    StringValue encoding = getIni("unicode.http_input_encoding");

    if (encoding.length() == 0) {
      encoding = getIni("unicode.fallback_encoding");

      if (encoding.length() == 0)
        encoding = UTF8_STRING;
    }

    return encoding.toString();
  }

  /**
   * Returns the encoding used for output, null if unicode.semantics is off.
   */
  public String getOutputEncoding()
  {
    if (! _isUnicodeSemantics)
      return null;

    String encoding = Quercus.INI_UNICODE_OUTPUT_ENCODING.getAsString(this);

    if (encoding == null)
      encoding = Quercus.INI_UNICODE_FALLBACK_ENCODING.getAsString(this);

    if (encoding == null)
      encoding = "utf-8";

    return encoding;
  }

  /**
   * Creates a binary builder.
   */
  public StringValue createBinaryBuilder()
  {
    if (_isUnicodeSemantics)
      return new BinaryBuilderValue();
    else
      return new StringBuilderValue();
  }

  /**
   * Creates a binary builder for large things like files.
   */
  public StringValue createLargeBinaryBuilder()
  {
    if (_isUnicodeSemantics)
      return new BinaryBuilderValue();
    else
      return new LargeStringBuilderValue();
  }

  /**
   * Creates a binary builder.
   */
  public StringValue createBinaryBuilder(int length)
  {
    if (_isUnicodeSemantics)
      return new BinaryBuilderValue(length);
    else
      return new StringBuilderValue(length);
  }

  /**
   * Creates a binary builder.
   */
  public StringValue createBinaryBuilder(byte []buffer, int offset, int length)
  {
    if (_isUnicodeSemantics)
      return new BinaryBuilderValue(buffer, offset, length);
    else
      return new StringBuilderValue(buffer, offset, length);
  }

  /**
   * Creates a binary builder.
   */
  public StringValue createBinaryBuilder(byte []buffer)
  {
    if (_isUnicodeSemantics)
      return new BinaryBuilderValue(buffer, 0, buffer.length);
    else
      return new StringBuilderValue(buffer, 0, buffer.length);
  }

  /**
   * Creates a unicode builder.
   */
  public StringValue createUnicodeBuilder()
  {
    if (_isUnicodeSemantics)
      return new UnicodeBuilderValue();
    else
      return new StringBuilderValue();
  }

  public TimeZone getDefaultTimeZone()
  {
    return _defaultTimeZone;
  }

  public QDate getGmtDate()
  {
    if (_gmtDate == null) {
      _gmtDate = _freeGmtDateList.allocate();

      if (_gmtDate == null)
        _gmtDate = new QDate();
    }

    return _gmtDate;
  }

  public QDate getLocalDate()
  {
    if (_localDate == null) {
      _localDate = _freeLocalDateList.allocate();

      if (_localDate == null)
        _localDate = QDate.createLocal();
    }

    return _localDate;
  }
 
  public void setDefaultTimeZone(String id)
  {
    _defaultTimeZone = TimeZone.getTimeZone(id);
  }
 
  public void setDefaultTimeZone(TimeZone zone)
  {
    _defaultTimeZone = zone;
  }

  /*
   * Returns the ServletContext.
   */
  public ServletContext getServletContext()
  {
    return _quercus.getServletContext();
  }
 
  /*
   * Sets the ScriptContext.
   */
  public void setScriptContext(ScriptContext context)
  {
    _scriptContext = context;
  }
 
  /*
   * Returns the input (POST, PUT) data.
   */
  public StringValue getInputData()
  {
    return _inputData;
  }
 
  /*
   * Sets the post data.
   */
  public void setInputData(StringValue data)
  {
    _inputData = data;
  }

  /**
   * Returns true for strict mode.
   */
  public final boolean isStrict()
  {
    return _isStrict;
  }
 
  /*
   * Returns true if allowed to include urls.
   */
  public boolean isAllowUrlInclude()
  {
    return _isAllowUrlInclude;
  }
 
  /*
   * Returns true if allowed to fopen urls.
   */
  public boolean isAllowUrlFopen()
  {
    return _isAllowUrlFopen;
  }

  /**
   * Returns the connection status
   */
  public int getConnectionStatus()
  {
    // always returns a valid value for now
    return 0;
  }
 
  public void start()
  {
    _oldThreadEnv = _threadEnv.get();
   
    _startTime = System.currentTimeMillis();
    _timeLimit = getIniLong("max_execution_time") * 1000;

    if (_timeLimit > 0)
      _endTime = _startTime + _timeLimit;
    else
      _endTime = Long.MAX_VALUE / 2;
   
    _threadEnv.set(this);
   
    // quercus/1b06
    String encoding = getOutputEncoding();
   
    String type = getIniString("default_mimetype");
   
    if ("".equals(type) || _response == null) {
    }
    else if (encoding != null)
      _response.setContentType(type + "; charset=" + encoding);
    else
      _response.setContentType(type);

    if (_out != null && encoding != null) {
      try {
        _out.setEncoding(encoding);
      } catch (Exception e) {
        log.log(Level.WARNING, e.toString(), e);
      }
    }
       
    HashSet<ModuleStartupListener> listeners
      = _quercus.getModuleStartupListeners();

    for (ModuleStartupListener listener : listeners)
      listener.startup(this);
  }

  /**
   * add resource to the list of refrences that are
   * cleaned up when finished with this environment.
   */
  public void addCleanup(EnvCleanup envCleanup)
  {
    if (_cleanupList == null)
      _cleanupList = new ArrayList<EnvCleanup>();
   
    _cleanupList.add(envCleanup);
  }

  /**
   * add an object with a destructor to the list of references that are
   * cleaned up when finished with this environment.
   */
  public void addObjectCleanup(ObjectExtValue objCleanup)
  {
    if (_objCleanupList == null)
      _objCleanupList = new ArrayList<ObjectExtValue>();
   
    _objCleanupList.add(objCleanup);
  }

  /**
   * remove resource from the list of references that are
   * cleaned up when finished with this environment.
   *
   * @param resource
   */
  public void removeCleanup(EnvCleanup envCleanup)
  {
    if (_cleanupList == null)
      return;
   
    for (int i = _cleanupList.size() - 1; i >= 0; i--) {
      EnvCleanup res = _cleanupList.get(i);

      if (envCleanup.equals(res)) {
        _cleanupList.remove(i);
        break;
      }
    }
  }

  /**
   * Returns the owning PHP engine.
   */
  public Quercus getQuercus()
  {
    return _quercus;
  }

  /**
   * Returns the owning PHP engine.
   */
  public ModuleContext getModuleContext()
  {
    return _quercus.getModuleContext();
  }

  /**
   * Returns the configured database.
   */
  public DataSource getDatabase()
  {
    return _quercus.getDatabase();
  }

  protected final DataSource findDatabase(String driver, String url)
    throws Exception
  {
    return _quercus.findDatabase(driver, url);
  }
 
  /**
   * Returns a connection to the given database. If there is
   * already a connection to this specific database, then
   * return the connection from the pool. Otherwise, create
   * a new connection and add it to the pool.
   */
  public ConnectionEntry getConnection(String driver, String url,
                                       String userName, String password,
                                       boolean isReuse)
    throws Exception
  {
    // XXX: connections might not be reusable (see gallery2), because
    // of case with two reuses and one closes because of CREATE or
    // catalog
    isReuse = false;
   
    DataSource database = _quercus.getDatabase();

    if (database != null) {
      userName = null;
      password = null;
    }
    else {
      database = findDatabase(driver, url);

      if (database == null)
        return null;
    }
   
    ConnectionEntry entry = new ConnectionEntry(this);
    entry.init(database, userName, password);

    ConnectionEntry oldEntry = null;

    if (isReuse)
      oldEntry = _connMap.get(entry);

    if (oldEntry != null && oldEntry.isReusable())
      return oldEntry;

    entry.connect(isReuse);

    if (isReuse)
      _connMap.put(entry, entry);

    return entry;
  }

  /**
   * Returns the configured database.
   */
  public DataSource getDataSource(String driver, String url)
    throws Exception
  {
    DataSource database = _quercus.getDatabase();

    if (database != null)
      return database;
    else
      return findDatabase(driver, url);
  }

  /**
   * Sets the time limit.
   */
  public void setTimeLimit(long ms)
  {
    if (ms <= 0)
      ms = Long.MAX_VALUE / 2;

    _timeLimit = ms;
  }

  /**
   * Checks for the program timeout.
   */
  public final void checkTimeout()
  {
    long now = System.currentTimeMillis();

    if (_endTime < now)
      throw new QuercusRuntimeException(L.l("script timed out"));
  }

  public void resetTimeout()
  {
    _startTime = System.currentTimeMillis();
    _endTime = _startTime + _timeLimit;
  }
 
  /**
   * Returns the writer.
   */
  public WriteStream getOut()
  {
    return _out;
  }

  /**
   * Returns the writer.
   */
  public WriteStream getOriginalOut()
  {
    return _originalOut;
  }

  /**
   * Flushes the output buffer.
   */
  public final void flush()
  {
    try {
      getOut().flush();
    } catch (IOException e) {
      throw new QuercusModuleException(e);
    }
  }

  /**
   * Prints a string
   */
  public final void print(String v)
  {
    try {
      getOut().print(v);
    } catch (IOException e) {
      throw new QuercusModuleException(e);
    }
  }

  /**
   * Prints a character buffer.
   */
  public final void print(char []buffer, int offset, int length)
  {
    try {
      getOut().print(buffer, offset, length);
    } catch (IOException e) {
      throw new QuercusModuleException(e);
    }
  }

  /**
   * Prints a char
   */
  public final void print(char v)
  {
    try {
      getOut().print(v);
    } catch (IOException e) {
      throw new QuercusModuleException(e);
    }
  }

  /**
   * Prints a long
   */
  public final void print(long v)
  {
    try {
      getOut().print(v);
    } catch (IOException e) {
      throw new QuercusModuleException(e);
    }
  }

  /**
   * Prints a double
   */
  public final void print(double v)
  {
    try {
      long longV = (long) v;
     
      if (v == longV)
        getOut().print(longV);
      else
        getOut().print(v);
    } catch (IOException e) {
      throw new QuercusModuleException(e);
    }
  }

  /**
   * Prints an object
   */
  public final void print(Object v)
  {
    try {
      getOut().print(v);
    } catch (IOException e) {
      throw new QuercusModuleException(e);
    }
  }

  /**
   * Prints a value
   */
  public final void print(Value v)
  {
    v.print(this);
  }

  /**
   * Prints a string
   */
  public final void println()
  {
    try {
      getOut().println();
    } catch (IOException e) {
      throw new QuercusModuleException(e);
    }
  }

  /**
   * Prints a string
   */
  public final void println(String v)
  {
    try {
      getOut().println(v);
    } catch (IOException e) {
      throw new QuercusModuleException(e);
    }
  }

  /**
   * Prints a string
   */
  public final void println(Value v)
  {
    try {
      v.print(this);
      getOut().println();
    } catch (IOException e) {
      throw new QuercusModuleException(e);
    }
  }

  /**
   * Prints and object.
   */
  public final void println(Object v)
  {
    try {
      getOut().println(v);
    } catch (IOException e) {
      throw new QuercusModuleException(e);
    }
  }

  /**
   * Prints a byte buffer.
   */
  public final void write(byte []buffer, int offset, int length)
  {
    try {
      getOut().write(buffer, offset, length);
    } catch (IOException e) {
      throw new QuercusModuleException(e);
    }
  }

  /**
   * Returns the current output buffer.
   */
  public OutputBuffer getOutputBuffer()
  {
    return _outputBuffer;
  }

  /**
   * Returns the writer.
   */
  public void pushOutputBuffer(Callback callback, int chunkSize, boolean erase)
  {
    if (_outputBuffer == null) {
      _outputBuffer =
        new OutputBuffer(_outputBuffer, this, callback, chunkSize, erase);
    }
    else
      _outputBuffer =
        new OutputBuffer(_outputBuffer, this, callback, chunkSize, erase);

    _out = _outputBuffer.getOut();
  }

  /**
   * Pops the output buffer
   */
  public boolean popOutputBuffer()
  {
    OutputBuffer outputBuffer = _outputBuffer;

    if (outputBuffer == null)
      return false;

    outputBuffer.close();

    _outputBuffer = outputBuffer.getNext();

    if (_outputBuffer != null)
      _out = _outputBuffer.getOut();
    else {
      _out = _originalOut;
    }

    return true;
  }

  /**
   * Returns the current directory.
   */
  public Path getPwd()
  {
    return _pwd;
  }

  public String getShellPwd()
  {
    if (_pwd instanceof MemoryPath)
      return System.getProperty("user.dir");
    else
      return _pwd.getNativePath();
  }

  /**
   * Returns the current directory.
   */
  public Path getWorkDir()
  {
    return _quercus.getWorkDir();
  }

  /**
   * Sets the current directory.
   */
  public void setPwd(Path path)
  {
    _pwd = path;
    _lookupCache.clear();
  }

  /**
   * Returns the initial directory.
   */
  public Path getSelfPath()
  {
    return _selfPath;
  }

  /**
   * Returns the initial directory.
   */
  public Path getSelfDirectory()
  {
    return _selfDirectory;
  }

  /**
   * Sets the initial directory.
   */
  public void setSelfPath(Path path)
  {
    _selfPath = path;
    _selfDirectory = _selfPath.getParent();
  }

  /**
   * Returns the upload directory.
   */
  public Path getUploadDirectory()
  {
    if (_uploadPath == null) {
      String realPath = getIniString("upload_tmp_dir");

      if (realPath == null)
        realPath = getServletContext().getRealPath("/WEB-INF/upload");

      _uploadPath = _quercus.getPwd().lookup(realPath);

      try {
        if (! _uploadPath.isDirectory())
          _uploadPath.mkdirs();
      }
      catch (IOException e) {
        log.log(Level.FINE, e.toString(), e);
      }

      _uploadPath = _uploadPath.createRoot();
    }

    return _uploadPath;
  }
 
  /*
   * Returns the temp directory (used by tmpfile()).
   */
  public Path getTempDirectory()
  {
    String realPath;
   
    if (_tmpPath == null) {
      if (getRequest() != null)
        realPath = getServletContext().getRealPath("/WEB-INF/tmp");
      else
        realPath = "file:/tmp";
     
      _tmpPath = getPwd().lookup(realPath);
     
      try {
        if (! _tmpPath.isDirectory())
          _tmpPath.mkdirs();
      }
      catch (IOException e) {
        log.log(Level.FINE, e.toString(), e);
      }
    }
   
    return _tmpPath;
  }

  /**
   * Adds an auto-remove path.
   */
  public void addRemovePath(Path path)
  {
    if (_removePaths == null)
      _removePaths = new ArrayList<Path>();

    _removePaths.add(path);
  }

  /**
   * Returns the request.
   */
  public HttpServletRequest getRequest()
  {
    return _request;
  }

  /**
   * Returns the most recently modified time of all of the {@link Path}'s that
   * have been used for this Env, or 0 if that cannot be determined.
   */
  /*
  public long getLastModified()
  {
    long lastModified = 0;

    if (_page != null) {
      Path pagePath = _page.getSelfPath(this);

      if (pagePath != null)
        lastModified = pagePath.getLastModified();
    }

    for (Path includePath : _includeSet) {
      long includeLastModified = includePath.getLastModified();

      if (lastModified < includeLastModified)
        lastModified = includeLastModified;
    }

    return lastModified;
  }
  */

  /**
   * Returns the response.
   */
  public HttpServletResponse getResponse()
  {
    return _response;
  }

  /**
   * Sets the session callback.
   */
  public void setSessionCallback(SessionCallback callback)
  {
    _sessionCallback = callback;
  }

  /**
   * Gets the session callback.
   */
  public SessionCallback getSessionCallback()
  {
    return _sessionCallback;
  }

  /**
   * Returns the session.
   */
  public SessionArrayValue getSession()
  {
    return _session;
  }

  /**
   * Returns the Java Http session.
   */
  public HttpSession getJavaSession()
  {
    return _javaSession;
  }

  /**
   * Sets the session.
   */
  public void setSession(SessionArrayValue session)
  {
    _session = session;

    if (session != null) {
      Value var = getGlobalVar("_SESSION");
     
      if (! (var instanceof SessionVar)) {
        var = new SessionVar();
        setGlobalValue("_SESSION", var);
      }

      var.set(session);

      setGlobalValue("HTTP_SESSION_VARS", session);

      session.addUse();
    }
    else {
      // php/1k0v
      Value v = getGlobalVar("_SESSION");

      if (v != null)
        v.set(UnsetValue.UNSET);

      v = getGlobalVar("HTTP_SESSION_VARS");

      if (v != null)
        v.set(UnsetValue.UNSET);
    }
  }

  /**
   * Returns a new session id.
   */
  public String generateSessionId()
  {
    String sessionId =
      _quercus.getQuercusSessionManager().createSessionId(this);

    if (_javaSession != null)
      sessionId = _javaSession.getId().substring(0, 3) + sessionId.substring(3);

    return sessionId;
  }

  /**
   * Create the session.
   */
  public SessionArrayValue createSession(String sessionId, boolean create)
  {
    long now = System.currentTimeMillis();

    SessionCallback callback = getSessionCallback();

    _javaSession = _request.getSession(true);

    if (create && _javaSession.getId().length() >= 3
               && sessionId.length() >= 3)
      sessionId = _javaSession.getId().substring(0, 3) + sessionId.substring(3);

    SessionArrayValue session = _quercus.loadSession(this, sessionId);

    if (callback != null) {
      StringValue value = callback.read(this, sessionId);

      if (value != null && value.length() != 0) {
        Value unserialize = VariableModule.unserialize(this, value);

        if (unserialize instanceof ArrayValue) {
          ArrayValue arrayValue = (ArrayValue) unserialize;

          session.reset(now);
          session.putAll(arrayValue);
        }
      }
    }

    setSession(session);

    return session;
  }

  /**
   * Destroy the session.
   */
  public void destroySession(String sessionId)
  {
    SessionCallback callback = getSessionCallback();

    if (callback != null) {
      callback.destroy(this, sessionId);
    }
    else {
      _quercus.destroySession(sessionId);
    }

    setSession(null);
  }

  /**
   * Returns the logger used for syslog.
   */
  public Logger getLogger()
  {
    if (_logger == null)
      _logger = Logger.getLogger("quercus.quercus");

    return _logger;
  }

  /**
   * Returns the configuration value of an init var.
   */
  public Value getConfigVar(String name)
  {
    return getIniDefinition(name).getValue(_quercus);
  }

  /**
   * 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 StringValue setIni(String name, Value value)
  {
    _iniCount++;
   
    StringValue oldValue = getIni(name);

    getIniDefinition(name).set(this, value);

    return oldValue;
  }

  /**
   * Sets an ini value.
   */
  public StringValue setIni(String name, String value)
  {
    _iniCount++;
   
    StringValue oldValue = getIni(name);

    getIniDefinition(name).set(this, value);

    return oldValue;
  }

  /**
   * Returns an ini value.
   */
  public StringValue getIni(String name)
  {
    return getIniDefinition(name).getAsStringValue(this);
  }

  private IniDefinition getIniDefinition(String name)
  {
    return _quercus.getIniDefinitions().get(name);
  }

  /**
   * Returns an ini value.
   */
  public boolean getIniBoolean(String name)
  {
    return getIniDefinition(name).getAsBoolean(this);
  }

  /**
   * Returns an ini value as a long.
   */
  public long getIniLong(String name)
  {
    return getIniDefinition(name).getAsLong(this);
  }

  /**
   * Returns an ini value as a string, null for missing or empty string
   */
  public String getIniString(String name)
  {
    return getIniDefinition(name).getAsString(this);
  }

  /**
   * Returns an ini value.
   */
  public long getIniBytes(String name, long deflt)
  {
    return getIniDefinition(name).getAsLongBytes(this, deflt);
  }

  /**
   * Returns the ByteToChar converter.
   */
  public ByteToChar getByteToChar()
  {
    return ByteToChar.create();
  }

  /**
   * Returns the 'this' value.
   */
  public Value getThis()
  {
    return _this;
  }

  /**
   * Sets the 'this' value, returning the old value.
   */
  public Value setThis(Value value)
  {
    Value oldThis = _this;

    _this = value.toValue();

    return oldThis;
  }

  /**
   * Gets a value.
   */
  public Value getValue(String name)
  {
    EnvVar var = getEnvVar(name);

    // XXX: not auto-create?

    return var.get();
   
    /*
    if (var != null)
      return var.toValue();
    else
      return NullValue.NULL;
    */
  }

  /**
   * Gets a special value, a special value is used to store and retrieve module
   * specific values in the env using a unique name.
   */
  public Object getSpecialValue(String name)
  {
    return _specialMap.get(name);
  }

  /**
   * Gets a global
   */
  public Value getGlobalValue(String name)
  {
    EnvVar var = getGlobalEnvVar(name);

    // XXX: don't allocate?

    return var.get();

    /*
    if (var != null)
      return var.toValue();
    else
      return NullValue.NULL;
    */
  }

  /**
   * Gets a variable
   *
   * @param name the variable name
   * @param var the current value of the variable
   */
  public final Var getVar(String name, Value value)
  {
    if (value != null)
      return (Var) value;

    return getRef(name);
  }

  /**
   * Gets a variable
   *
   * @param name the variable name
   * @param value the current value of the variable
   */
  public final Var getGlobalVar(String name, Value value)
  {
    if (value != null)
      return (Var) value;

    return getGlobalRef(name);
  }

  /**
   * Gets a value.
   */
  public Var getRef(String name)
  {
    EnvVar envVar = getEnvVar(name);

    // XXX: can return null?
   
    return envVar.getRef();

    /*
    Var var = _map.get(name);

    // required for $$ref where $ref is the name of a superglobal
    if (var == null) {
      // php/0809
      if (Quercus.isSuperGlobal(name))
        return getGlobalRef(name);
    }

    return var;
    */
  }
 
  /**
   * Gets a value.
   */
  public Var getRef(String name, boolean isAutoCreate)
  {
    EnvVar envVar = getEnvVar(name, isAutoCreate);

    if (envVar != null)
      return envVar.getRef();
    else
      return null;
  }

  /**
   * Returns the raw global lookup.
   */
  public EnvVar getGlobalRaw(String name)
  {
    return _globalMap.get(name);
  }
 
  /**
   * Gets a global value.
   */
  public Var getGlobalRef(String name)
  {
    EnvVar envVar = getGlobalEnvVar(name);

    // XXX: not create?

    return envVar.getRef();
  }

  public final EnvVar getEnvVar(String name)
  {
    return getEnvVar(name, true);
  }
 
  /**
   * Gets a variable
   *
   * @param name the variable name
   * @param var the current value of the variable
   */
  public final EnvVar getEnvVar(String name, boolean isAutoCreate)
  {
    EnvVar envVar = _map.get(name);

    if (envVar != null)
      return envVar;

    if (_map == _globalMap)
      return getGlobalEnvVar(name, isAutoCreate);
   
    envVar = getSuperGlobalRef(name, true);

    // php/0809
    if (envVar != null)
      _globalMap.put(name, envVar);
    else if (! isAutoCreate)
      return null;
    else
      envVar = new EnvVarImpl(new Var());

    _map.put(name, envVar);

    return envVar;
  }

  /**
   * Gets a variable
   *
   * @param name the variable name
   */
  public final EnvVar getGlobalEnvVar(String name)
  {
    return getGlobalEnvVar(name, true);
  }
 
  /**
   * Gets a variable
   *
   * @param name the variable name
   * @param isAutoCreate
   */
  public final EnvVar getGlobalEnvVar(String name, boolean isAutoCreate)
  {
    EnvVar envVar = _globalMap.get(name);

    if (envVar != null)
      return envVar;
     
    envVar = getSuperGlobalRef(name);

    if (envVar == null) {
      // variables set by the caller, e.g. the servlet
     
      Value value = _scriptGlobalMap.get(name);

      if (value != null) {
        envVar = new EnvVarImpl(new Var());
        envVar.setRef(value);
      }
    }

    if (envVar == null)
      envVar = getGlobalScriptContextRef(name);

    if (envVar == null) {
      if (! isAutoCreate)
        return null;
     
      Var var = new Var();
      var.setGlobal();
     
      envVar = new EnvVarImpl(var);
    }
   
    _globalMap.put(name, envVar);

    return envVar;
  }

  /**
   * Pushes a new environment.
   */
  public Map<String,EnvVar> pushEnv(Map<String,EnvVar> map)
  {
    Map<String,EnvVar> oldEnv = _map;

    _map = map;

    return oldEnv;
  }

  /**
   * Restores the old environment.
   */
  public void popEnv(Map<String,EnvVar> oldEnv)
  {
    _map = oldEnv;
  }

  /**
   * Returns the current environment.
   */
  public Map<String,EnvVar> getEnv()
  {
    return _map;
  }

  /**
   * Returns the current environment.
   */
  public Map<String,EnvVar> getGlobalEnv()
  {
    return _globalMap;
  }

  public boolean isGlobalEnv()
  {
    return _map == _globalMap;
  }

  /**
   * Gets a static variable name.
   */
  public final String createStaticName()
  {
    return _quercus.createStaticName();
  }

  /**
   * Gets a static variable
   *
   * @param name the variable name
   */
  public final Var getStaticVar(String name)
  {
    return getGlobalVar(name);
  }

  /**
   * Unsets variable
   *
   * @param name the variable name
   */
  public final Var unsetVar(String name)
  {
    EnvVar envVar = _map.get(name);

    if (envVar != null)
      envVar.setRef(new Var());

    return null;
  }

  /**
   * Gets a variable
   *
   * @param name the variable name
   * @param value the current value of the variable
   */
  public final Var setVar(String name, Value value)
  {
    throw new UnsupportedOperationException();
   
    /*
    Var var;

    if (value instanceof Var) {
      var = (Var) value;

      if (_map == _globalMap)
        var.setGlobal();
    }
    else
      var = new Var(value.toValue());

    _map.put(name, var);

    return var;
    */
  }

  /**
   * Unsets variable
   *
   * @param name the variable name
   */
  public final Var unsetLocalVar(String name)
  {
    EnvVar envVar = _map.get(name);

    if (envVar != null)
      envVar.setRef(new Var());

    return null;
  }

  /**
   * Unsets variable
   *
   * @param name the variable name
   */
  public final Var unsetGlobalVar(String name)
  {
    EnvVar envVar = _globalMap.get(name);

    if (envVar != null)
      envVar.setRef(new Var());

    return null;
  }

  /**
   * Gets a local
   *
   * @param var the current value of the variable
   */
  public static final Value getLocalVar(Value var)
  {
    if (var == null)
      var = new Var();

    return var;
  }

  /**
   * Gets a local value
   *
   * @param var the current value of the variable
   */
  public static final Value getLocalValue(Value var)
  {
    if (var != null)
      return var;
    else
      return NullValue.NULL;
  }

  /**
   * Gets a local
   *
   * @param var the current value of the variable
   */
  public static final Value setLocalVar(Value var, Value value)
  {
    value = value.toValue();

    if (var instanceof Var)
      var.set(value);

    return value;
  }

  private EnvVar getSuperGlobalRef(String name)
  {
    return getSuperGlobalRef(name, false);
  }
 
  /**
   * Returns a superglobal.
   */
  @SuppressWarnings("unchecked")
  private EnvVar getSuperGlobalRef(String name, boolean isCheckGlobal)
  {
    Var var;
    EnvVar envVar;
   
    int specialVarId = SPECIAL_VARS.get(name);
   
    if (isCheckGlobal) {
      if (specialVarId != IntMap.NULL) {
        envVar = _globalMap.get(name);
       
        if (envVar != null)
          return envVar;
      }
    }

    String encoding = getHttpInputEncoding();
   
    if (encoding == null)
      encoding = "iso-8859-1";
   
   
    switch (specialVarId) {
      case _ENV: {
        var = new Var();
        envVar = new EnvVarImpl(var);

        _globalMap.put(name, envVar);

        envVar.set(new ArrayValueImpl());

        return envVar;
      }

      case HTTP_POST_VARS:
        if (! Quercus.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this))
          return null;
        else
          return getGlobalEnvVar("_POST");
       
      case _POST: {
        var = new Var();
        envVar = new EnvVarImpl(var);

        _globalMap.put(name, envVar);

        ArrayValue post = new ArrayValueImpl();

        envVar.set(post);

        if (_request == null)
          return null;

        if (! "POST".equals(_request.getMethod()))
          return envVar;

        if (_postArray != null) {
          for (Map.Entry<Value, Value> entry : _postArray.entrySet()) {
            post.put(entry.getKey(), entry.getValue());
          }
        }
       
        return envVar;
      }

      case HTTP_POST_FILES:
        if (! Quercus.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this))
          return null;
        else
          return getGlobalEnvVar("_FILES");
       
      case _FILES: {
        var = new Var();
        envVar = new EnvVarImpl(var);

        _globalMap.put(name, envVar);

        ArrayValue files = new ArrayValueImpl();

        if (_files != null) {
          for (Map.Entry<Value, Value> entry : _files.entrySet()) {
            files.put(entry.getKey(), entry.getValue());
          }
        }

        envVar.set(files);
       
        return envVar;
      }

      case HTTP_GET_VARS:
        if (! Quercus.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this))
          return null;
        else
          return getGlobalEnvVar("_GET");
     
      case _GET: {
        if (isCheckGlobal) {
          EnvVar e = _globalMap.get(name);
         
          if (e != null)
            return e;
        }
       
        var = new Var();
        envVar = new EnvVarImpl(var);

        _globalMap.put(name, envVar);

        ArrayValue array = new ArrayValueImpl();

        envVar.set(array);

        if (_request == null)
          return envVar;
       
        String queryString = _request.getQueryString();
        if (queryString == null)
          return envVar;

        StringUtility.parseStr(this,
                               queryString,
                               array,
                               true,
                               getHttpInputEncoding());
       
        return envVar;
      }
     
      case _REQUEST: {
        var = new Var();
        envVar = new EnvVarImpl(var);

        ArrayValue array = new ArrayValueImpl();

        envVar.set(array);

        _globalMap.put(name, envVar);

        if (_request == null)
          return envVar;
       
        try {
          _request.setCharacterEncoding(encoding);
        } catch (Exception e) {
          log.log(Level.FINE, e.toString(), e);
        }
       
        ArrayList<String> keys = new ArrayList<String>();
        keys.addAll((Set<String>)_request.getParameterMap().keySet());

        Collections.sort(keys);

        boolean isMagicQuotes = getIniBoolean("magic_quotes_gpc");
       
        for (String key : keys) {
          String []value = _request.getParameterValues(key);

          Post.addFormValue(this,
                            array,
                            key,
                            value,
                            isMagicQuotes,
                            encoding);
        }

        if (name.equals("_REQUEST") && _postArray != null) {
         
          for (Map.Entry<Value, Value> entry : _postArray.entrySet()) {
            Value key = entry.getKey();
            Value value = entry.getValue();
           
            Value existingValue = array.get(key);
           
            if (existingValue.isArray() && value.isArray())
             existingValue.toArrayValue(this).putAll(value.toArrayValue(this));
            else
              array.put(entry.getKey(), entry.getValue().copy());
          }
        }

        Cookie []cookies = _request.getCookies();
        for (int i = 0; cookies != null && i < cookies.length; i++) {  
          Cookie cookie = cookies[i];

          String decodedValue = decodeValue(cookie.getValue());

          Post.addFormValue(this,
                            array,
                            cookie.getName(),
                            new String[] { decodedValue },
                            isMagicQuotes,
                            encoding);
        }
       
        return envVar;
      }

      case HTTP_RAW_POST_DATA: {
        if (! Quercus.INI_ALWAYS_POPULATE_RAW_POST_DATA.getAsBoolean(this))
          return null;
       
        if (_inputData == null)
          return null;
       
        var = new Var();
        envVar = new EnvVarImpl(var);

        _globalMap.put(name, envVar);
       
        var.set(_inputData);
       
        return envVar;
      }
   
      case HTTP_SERVER_VARS:
        if (! Quercus.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this))
          return null;
        else
          return getGlobalEnvVar("_SERVER");
     
      case _SERVER: {
        var = new Var();
        envVar = new EnvVarImpl(var);

        _globalMap.put(name, envVar);

        var.set(new ServerArrayValue(this));

        return envVar;
      }

      case _GLOBAL: {
        var = new Var();
        envVar = new EnvVarImpl(var);

        _globalMap.put(name, envVar);

        var.set(new GlobalArrayValue(this));

        return envVar;
      }

      case HTTP_COOKIE_VARS:
        if (! Quercus.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this))
          return null;
        else
          return getGlobalEnvVar("_COOKIE");
     
      case _COOKIE: {
        var = new Var();
        envVar = new EnvVarImpl(var);

        _globalMap.put(name, envVar);

        if (_request == null)
          return envVar;
       
        ArrayValue array = new ArrayValueImpl();

        Cookie []cookies = _request.getCookies();
        if (cookies != null) {
          for (int i = 0; i < cookies.length; i++) {
            Cookie cookie = cookies[i];

            String value = decodeValue(cookie.getValue());

            StringValue valueAsValue = createString(value, encoding);

            if (getIniBoolean("magic_quotes_gpc")) // php/0876
              valueAsValue = StringModule.addslashes(valueAsValue);

            array.append(createString(cookie.getName(), encoding), valueAsValue);
          }
        }

        var.set(array);

        return envVar;
      }
     
      case _SESSION: {
        return _globalMap.get("_SESSION");
      }

      case PHP_SELF: {
        var = new Var();
        envVar = new EnvVarImpl(var);

        _globalMap.put(name, envVar);

        var.set(getGlobalVar("_SERVER").get(PHP_SELF_STRING));

        return envVar;
      }
     
      default:
        return null;
    }
  }

  /**
   * Gets a value.
   */
  protected EnvVar getGlobalSpecialRef(String name)
  {
    if (Quercus.isSuperGlobal(name))
      return _globalMap.get(name);
    else
      return null;
  }

  protected EnvVar getGlobalScriptContextRef(String name)
  {
    if (_scriptContext == null)
      return null;

    EnvVar envVar = _globalMap.get(name);
   
    if (envVar != null)
      return envVar;
   
    Object value = _scriptContext.getAttribute(name);

    if (value == null) {
      Bindings bindings
        = _scriptContext.getBindings(ScriptContext.ENGINE_SCOPE);

      if (bindings != null)
        value = bindings.get(name);
    }

    if (value == null) {
      Bindings bindings
      = _scriptContext.getBindings(ScriptContext.GLOBAL_SCOPE);

      if (bindings != null)
        value = bindings.get(name);
    }

    if (value != null) {
      envVar = new EnvVarImpl(new Var());
     
      _globalMap.put(name, envVar);
     
      envVar.set(wrapJava(value));
    }

    return envVar;
  }

  private static String decodeValue(String s)
  {
    int len = s.length();
    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < len; i++) {
      char ch = s.charAt(i);

      if (ch == '%' && i + 2 < len) {
        int d1 = s.charAt(i + 1);
        int d2 = s.charAt(i + 2);

        int v = 0;

        if ('0' <= d1 && d1 <= '9')
          v = 16 * (d1 - '0');
        else if ('a' <= d1 && d1 <= 'f')
          v = 16 * (d1 - 'a' + 10);
        else if ('A' <= d1 && d1 <= 'F')
          v = 16 * (d1 - 'A' + 10);
        else {
          sb.append('%');
          continue;
        }

        if ('0' <= d2 && d2 <= '9')
          v += (d2 - '0');
        else if ('a' <= d2 && d2 <= 'f')
          v += (d2 - 'a' + 10);
        else if ('A' <= d2 && d2 <= 'F')
          v += (d2 - 'A' + 10);
        else {
          sb.append('%');
          continue;
        }

        i += 2;
        sb.append((char) v);
      }
      else if (ch == '+')
        sb.append(' ');
      else
        sb.append(ch);
    }

    return sb.toString();
  }

  /**
   * Gets a value.
   */
  public Var getVar(String name)
  {
    EnvVar envVar = getEnvVar(name);

    return envVar.getRef();
  }

  /**
   * Gets a value.
   */
  public Var getGlobalVar(String name)
  {
    EnvVar envVar = getGlobalEnvVar(name);

    return envVar.getRef();
  }

  /**
   * Sets a value.
   */
  public Value setValue(String name, Value value)
  {
    EnvVar envVar = getEnvVar(name);
   
    if (value instanceof Var)
      envVar.setRef((Var) value);
    else
      envVar.set(value);

    return value;
  }

  /**
   * Sets a value.
   */
  public Value setRef(String name, Value value)
  {
    EnvVar envVar = getEnvVar(name);
   
    if (value instanceof Var)
      envVar.setRef((Var) value);
    else
      envVar.set(value);

    return value;
  }

  /**
   * Sets a value.
   */
  public static Var toRef(Value value)
  {
    // php/3243
   
    if (value instanceof Var)
      return (Var) value;
    else
      return new Var(value);
  }

  /**
   * Sets a special value, a special value is used to store and retrieve module
   * specific values in the env using a unique name.
   */
  public Object setSpecialValue(String name, Object value)
  {
    _specialMap.put(name, value);

    return value;
  }

  /**
   * External calls to set a global value.
   */
  public Value setGlobalValue(String name, Value value)
  {
    EnvVar envVar = getGlobalEnvVar(name);

    envVar.setRef(value);

    return value;
  }

  /**
   * Gets a static class field.
   *
   * @param className of the owning class
   * @param name of the variable
   */
  public Value getStaticClassFieldValue(String className,
                                        String name)
  {
    return getStaticClassFieldValue(className + "::" + name,
                                    className,
                                    name);
  }

  /**
   * Gets a static class field.
   *
   * @param fieldName qualified field name, e.g. "Foo::bar"
   * @param className of the owning class
   * @param name of the variable
   */
  public Value getStaticClassFieldValue(String fieldName,
                                        String className,
                                        String name)
  {
    Var var = getStaticClassFieldVar(fieldName, className, name);
   
    if (var != null) {
      Value val = var.toValue();
       
      return val;
    }
    else
      return NullValue.NULL;
  }

  /**
   * Gets a static class field.
   *
   * @param className of the owning class
   * @param name of the variable
   */
  public Value getStaticClassFieldVar(String className,
                                        String name)
  {
    return getStaticClassFieldVar(className + "::" + name,
                                  className,
                                    name);
  }

  /**
   * Gets a static field from a class.
   *
   * @param className of the owning class
   * @param name of the variable
   */
  public final Var getStaticClassFieldVar(String fieldName,
                                          String className,
                                          String name)
  {
    EnvVar envVar = getGlobalRaw(fieldName);

    if (envVar != null)
      return envVar.getRef();
   
    QuercusClass cl = getClass(className);

    Var var = cl.getStaticField(this, name);
   
    if (var == null) {
      error(L.l("{0}::${1} is an undeclared static property", className, name));
    }

    return var;
  }

  /**
   * Sets the calling function expression.
   */
  public void pushCall(Expr call, Value obj, Value []args)
  {
    if (_callStack == null) {
      _callStack = new Expr[256];
      _callThisStack = new Value[256];
      _callArgStack = new Value[256][];
    }
   
    if (_callStack.length <= _callStackTop) {
      Expr []newStack = new Expr[2 * _callStack.length];
      System.arraycopy(_callStack, 0, newStack, 0, _callStack.length);
      _callStack = newStack;
     
      Value []newThisStack = new Value[2 * _callThisStack.length];
      System.arraycopy(_callThisStack,
                       0, newThisStack,
                       0, _callThisStack.length);
     
      _callThisStack = newThisStack;
     
      Value [][]newArgStack = new Value[2 * _callArgStack.length][];
      System.arraycopy(_callArgStack,
                       0, newArgStack,
                       0, _callArgStack.length);
     
      _callArgStack = newArgStack;
    }

    _callStack[_callStackTop] = call;
    _callThisStack[_callStackTop] = obj;
    _callArgStack[_callStackTop] = args;

    _callStackTop++;
  }

  /**
   * Pops the top call.
   */
  public Expr popCall()
  {
    if (_callStack == null)
      throw new IllegalStateException();
   
    return _callStack[--_callStackTop];
  }

  /**
   * Returns the stack depth.
   */
  public int getCallDepth()
  {
    return _callStackTop;
  }
 
  /**
   * Peeks at the the top call.
   */
  public Expr peekCall(int depth)
  {
    if (_callStackTop - depth > 0)
      return _callStack[_callStackTop - depth - 1];
    else
      return null;
  }
 
  /**
   * Peeks at the "this" top call.
   */
  public Value peekCallThis(int depth)
  {
    if (_callStackTop - depth > 0)
      return _callThisStack[_callStackTop - depth - 1];
    else
      return null;
  }
 
  /**
   * Peeks at the the top call.
   */
  public Value []peekArgs(int depth)
  {
    if (_callStackTop - depth > 0)
      return _callArgStack[_callStackTop - depth - 1];
    else
      return null;
  }

  //
  // allocations
  //

  /**
   * Allocate the free regexp
   */
  public RegexpState allocateRegexpState()
  {
    RegexpState state = _freeRegexpState;
    _freeRegexpState = null;

    return state;
  }

  /**
   * Free the free regexp
   */
  public void freeRegexpState(RegexpState state)
  {
    _freeRegexpState = state;
  }

  //
  // profiling
  //
 
  public void pushProfile(int id)
  {
  }
 
  public void popProfile(long nanos)
  {
  }
 
  /*
   * Returns true if <code>name</code> doesn't already exist on the
   * field __get() stack.
   */
  public boolean pushFieldGet(String className, StringValue fieldName)
  {
    FieldGetEntry entry = new FieldGetEntry(className, fieldName);
   
    if (_fieldGetList.contains(entry))
      return false;
    else {
      _fieldGetList.push(entry);
     
      return true;
    }
  }
 
  public void popFieldGet()
  {
    _fieldGetList.pop();
  }
 
  /*
   * Returns the calling class.
   */
  public QuercusClass getCallingClass()
  {
    return _callingClass;
  }
 
  /*
   * Sets the calling class.
   */
  public QuercusClass setCallingClass(QuercusClass cls)
  {
    QuercusClass oldCallingClass = _callingClass;
   
    _callingClass = cls;
   
    return oldCallingClass;
  }

  public ArrayList<String> getStackTrace()
  {
    ArrayList<String> trace = new ArrayList<String>();

    for (int i = _callStackTop - 1; i >= 0; i--) {
      String entry;
      Location location = _callStack[i].getLocation();
      String loc;

      if (location != null && location.getFileName() != null) {
        loc = (" (at " + location.getFileName()
               + ":" + location.getLineNumber() + ")");
      }
      else
        loc = "";
     
      if (_callThisStack[i] != null
          && ! "".equals(_callThisStack[i].toString())) {
        entry = _callThisStack[i] + "." + _callStack[i].toString() + loc;
      }
      else
        entry = _callStack[i].toString() + loc;

      trace.add(entry);
    }

    return trace;
  }

  /**
   * Pushes a new environment.
   */
  public final Value []setFunctionArgs(Value []args)
  {
    Value []oldArgs = _functionArgs;

    Value []newArgs = new Value[args.length];

    for (int i = 0; args != null && i < args.length; i++) {
      // php/3715, 3768
      newArgs[i] = args[i].toValue().toArgValue();
    }

    _functionArgs = newArgs;

    return oldArgs;
  }

  /**
   * Pushes a new environment.
   */
  public final Value []setFunctionArgsNoCopy(Value []args)
  {
    Value []oldArgs = _functionArgs;

    for (int i = 0; args != null && i < args.length; i++)
      args[i] = args[i].toValue();

    _functionArgs = args;

    return oldArgs;
  }

  /**
   * Pushes a new environment.
   */
  public final void restoreFunctionArgs(Value []args)
  {
    _functionArgs = args;
  }

  /**
   * Returns the function args.
   */
  public final Value []getFunctionArgs()
  {
    return _functionArgs;
  }

  /**
   * Removes a specialValue
   */
  public Object removeSpecialValue(String name)
  {
    return _specialMap.remove(name);
  }

  /**
   * Returns a constant.
   */
  public Value getConstant(String name)
  {
    Value value = getConstantImpl(name);

    if (value != null)
      return value;

    /* XXX:
       notice(L.l("Converting undefined constant '{0}' to string.",
       name));
    */

    value = createStringOld(name);

    return value;
  }

  /**
   * Returns true if the constant is defined.
   */
  public boolean isDefined(String name)
  {
    return getConstantImpl(name) != null;
  }

  /**
   * Returns a constant.
   */
  private Value getConstantImpl(String name)
  {
    int id = _quercus.getConstantId(name);
   
    if (id < _const.length) {
      Value value = _const[id];

      if (value != null)
        return value;
    }
   
    int lowerId = _quercus.getConstantLower(id);

    if (lowerId < _const.length) {
      Value value = _const[lowerId];
     
      if (value != null)
        return value;
    }

    return null;

    /*
    value = _quercus.getConstant(name);
    if (value != null)
      return value;

    if (_lowerConstMap != null) {
      value = _lowerConstMap.get(name.toLowerCase());

      if (value != null)
        return value;
    }

    return null;
    */
  }

  /**
   * Returns a constant.
   */
  public Value getConstant(int id)
  {
    if (_const.length <= id)
      return _quercus.getConstantName(id);
   
    Value value = _const[id];

    if (value != null)
      return value;

    int lowerId = _quercus.getConstantLower(id);

    value = _const[lowerId];
    if (value != null)
      return value;

    return _quercus.getConstantName(id);

    /*
    value = _quercus.getConstant(name);
    if (value != null)
      return value;

    if (_lowerConstMap != null) {
      value = _lowerConstMap.get(name.toLowerCase());

      if (value != null)
        return value;
    }

    return null;
    */
  }
 
  /**
   * Removes a constant.
   */
  public Value removeConstant(String name)
  {
    int id = _quercus.getConstantId(name);

    Value value = _const[id];
   
    _const[id] = null;
   
    return value;
  }

  /**
   * Sets a constant.
   */
  public Value addConstant(String name,
                           Value value,
                           boolean isCaseInsensitive)
  {
    int id = _quercus.getConstantId(name);

    return addConstant(id, value, isCaseInsensitive);
  }

  /**
   * Sets a constant.
   */
  public Value addConstant(StringValue name,
                           Value value,
                           boolean isCaseInsensitive)
  {
    int id = _quercus.getConstantId(name);

    return addConstant(id, value, isCaseInsensitive);
  }

  /**
   * Sets a constant.
   */
  public Value addConstant(int id,
                           Value value,
                           boolean isCaseInsensitive)
  {
    if (_const.length <= id) {
      Value []newConst = new Value[id + 256];
      System.arraycopy(_const, 0, newConst, 0, _const.length);
     
      _const = newConst;
    }
   
    if (_const[id] != null)
      return notice(L.l("cannot redefine constant {0}",
                        _quercus.getConstantName(id)));
   
    _const[id] = value;

    if (isCaseInsensitive) {
      int lowerId = _quercus.getConstantLower(id);
     
      if (lowerId >= _const.length) {
        Value []newConst = new Value[lowerId + 256];
        System.arraycopy(_const, 0, newConst, 0, _const.length);
       
        _const = newConst;
      }

      _const[lowerId] = value;
    }

    return value;
  }

  /**
   * Returns an array of the defined functions.
   */
  public ArrayValue getDefinedConstants()
  {
    ArrayValue result = new ArrayValueImpl();

    for (int i = 0; i < _const.length; i++) {
      if (_const[i] != null) {
        result.append(_quercus.getConstantName(i), _const[i]);
      }
    }

    return result;
  }

  /**
   * Returns true if an extension is loaded.
   */
  public boolean isExtensionLoaded(String name)
  {
    return getQuercus().isExtensionLoaded(name);
  }

  /**
   * Returns true if an extension is loaded.
   */
  public HashSet<String> getLoadedExtensions()
  {
    return getQuercus().getLoadedExtensions();
  }

  /**
   * Returns true if an extension is loaded.
   */
  public Value getExtensionFuncs(String name)
  {
    return getQuercus().getExtensionFuncs(name);
  }

  /**
   * Returns the default stream resource.
   */
  public StreamContextResource getDefaultStreamContext()
  {
    if (_defaultStreamContext == null)
      _defaultStreamContext = new StreamContextResource();

    return _defaultStreamContext;
  }

  //
  // function handling
  //
 
  public ArrayValue getDefinedFunctions()
  {
    ArrayValueImpl funs = new ArrayValueImpl();
    ArrayValueImpl system = new ArrayValueImpl();
    ArrayValueImpl user = new ArrayValueImpl();

    AbstractFunction []systemFuns = _quercus.getFunctionMap();
    AbstractFunction []envFuns = _fun;

    for (int i = 0; i < envFuns.length; i++) {
      if (i < systemFuns.length
          && systemFuns[i] != null
          && ! (systemFuns[i] instanceof UndefinedFunction)) {
        system.append(createStringOld(systemFuns[i].getName()));
      }
      else if (envFuns[i] != null
               && ! (envFuns[i] instanceof UndefinedFunction)) {
        user.append(createStringOld(envFuns[i].getName()));
      }
    }

    funs.append(createStringOld("internal"), system);
    funs.append(createStringOld("user"), user);

    return funs;
  }

  /**
   * Returns the function with a given name.
   *
   * Compiled mode normally uses the _fun array directly, so this call
   * is rare.
   */
  public AbstractFunction findFunction(String name)
  {
    int id = _quercus.findFunctionId(name);

    if (id >= 0) {
      if (id < _fun.length && ! (_fun[id] instanceof UndefinedFunction))
        return _fun[id];
      else
        return null;
    }

    /*
    AbstractFunction fun = _quercus.findFunctionImpl(name);

    if (fun != null)
      return fun;

    if (isStrict())
      return null;

    name = name.toLowerCase();
   
    id = _quercus.findFunctionId(name);

    if (id >= 0) {
      if (id < _fun.length && ! (_fun[id] instanceof UndefinedFunction))
        return _fun[id];
      else
        return null;
    }
   
    fun = _quercus.findLowerFunctionImpl(name);
   
    if (fun != null)
      return fun;
    */
   
    if (_anonymousFunMap != null)
      return _anonymousFunMap.get(name);
     
    return null;
  }
 
  public AbstractFunction getFunction(String name)
  {
    AbstractFunction fun = findFunction(name);

    if (fun != null)
      return fun;
    else
      throw createErrorException(L.l("'{0}' is an unknown function.", name));
  }
 
  public void updateFunction(int id, AbstractFunction fun)
  {
    if (_fun.length <= id) {
      AbstractFunction []oldFun = _fun;
     
      _fun = new AbstractFunction[id + 256];
      System.arraycopy(oldFun, 0, _fun, 0, oldFun.length);
    }

    if (_fun[id] == null)
      _fun[id] = fun;
  }
 
  /*
  public int getFunctionId(String name)
  {
    int id = _quercus.getFunctionId(name);

    if (_fun.length <= id) {
      AbstractFunction []oldFun = _fun;
     
      _fun = new AbstractFunction[id + 256];
      System.arraycopy(oldFun, 0, _fun, 0, oldFun.length);
    }

    AbstractFunction []defFuns = _quercus.getFunctionMap();

    if (_fun[id] == null)
      _fun[id] = defFuns[id];

    return id;
  }
 
  public int getFunctionIdCount()
  {
    return _quercus.getFunctionIdCount();
  }
  */

  /**
   * Finds the java reflection method for the function with the given name.
   *
   * @param name the method name
   * @return the found method or null if no method found.
   */
  public AbstractFunction getFunction(Value name)
  {
    name = name.toValue();

    if (name instanceof CallbackFunction)
      return ((CallbackFunction) name).getFunction();

    return getFunction(name.toString());
  }

  /*
  public DefinitionState getDefinitionState()
  {
    return _defState;
  }
  */
 
  public Value addFunction(String name, AbstractFunction fun)
  {
    AbstractFunction staticFun
      = _quercus.findLowerFunctionImpl(name.toLowerCase());

    if (staticFun != null)
      throw new QuercusException(L.l("can't redefine function {0}", name));
   
    int id = _quercus.getFunctionId(name);

    // XXX: anonymous/generated functions(?), e.g. like foo2431

    if (_fun.length <= id) {
      AbstractFunction []funMap = new AbstractFunction[id + 256];
      System.arraycopy(_fun, 0, funMap, 0, _fun.length);
      _fun = funMap;
    }

    if (_fun[id] != null && ! (_fun[id] instanceof UndefinedFunction))
      throw new QuercusException(L.l("can't redefine function {0}", name));

    _fun[id] = fun;

    return BooleanValue.TRUE;
  }
 
  public AbstractFunction createAnonymousFunction(String args, String code)
    throws IOException
  {
    if (_anonymousFunMap == null)
      _anonymousFunMap = new HashMap<String, AbstractFunction>();

    // PHP naming style for anonymous functions
    String name = "\u0000lamba" + (_anonymousFunMap.size() + 1);
   
    AbstractFunction fun = getQuercus().parseFunction(name, args, code);

    _anonymousFunMap.put(name, fun);
    return fun;
  }

  /**
   * Adds a function from a compiled include
   *
   * @param name the function name, must be an intern() string
   * @param lowerName the function name, must be an intern() string
   */
  public Value addFunctionFromPage(String name, String lowerName,
                                   AbstractFunction fun)
  {
    // XXX: skip the old function check since the include for compiled
    // pages is already verified.  Might have a switch here?
    /*
    AbstractFunction oldFun = _lowerFunMap.get(lowerName);

    if (oldFun == null)
      oldFun = _quercus.findLowerFunctionImpl(lowerName);

    if (oldFun != null) {
      throw new QuercusException(L.l("can't redefine function {0}", name));
    }

    _funMap.put(name, fun);

    if (! isStrict())
      _lowerFunMap.put(lowerName, fun);
    */

    return BooleanValue.TRUE;
  }

  //
  // method handling
  //

  /**
   * Finds the java reflection method for the function with the given name.
   *
   * @param className the class name
   * @param methodName the method name
   * @return the found method or null if no method found.
   */
  public AbstractFunction findMethod(String className, String methodName)
  {
    QuercusClass cl = findClass(className);

    if (cl == null) {
      error(L.l("'{0}' is an unknown class.", className));
      return null;
    }

    AbstractFunction fun = cl.findFunction(methodName);

    if (fun == null) {
      error(L.l("'{0}::{1}' is an unknown method.",
                className, methodName));
      return null;
    }

    return fun;
  }

  //
  // evaluation
  //
 
  /**
   * Compiles and evalutes the given code
   *
   * @param code the code to evalute
   * @return the result
   */
  public Value evalCode(String code)
    throws IOException
  {
    if (log.isLoggable(Level.FINER))
      log.finer(code);

    Quercus quercus = getQuercus();

    QuercusProgram program = quercus.parseEvalExpr(code);

    Value value = program.execute(this);
   
    if (value == null)
      return NullValue.NULL;
    else
      return value;
  }
 
  /**
   * Evaluates the top-level code
   *
   * @return the result
   */
  public Value executeTop()
  {
    Path oldPwd = getPwd();

    Path pwd = _page.getPwd(this);

    setPwd(pwd);
    try {
      return executePageTop(_page);
    } catch (QuercusLanguageException e) {
      log.log(Level.FINER, e.toString(), e);

      if (getExceptionHandler() != null) {
        try {
          getExceptionHandler().call(this, e.getValue());
        }
        catch (QuercusLanguageException e2) {
          uncaughtExceptionError(e2);
        }
      }
      else {
        uncaughtExceptionError(e);
      }
     
      return NullValue.NULL;
    } finally {
      setPwd(oldPwd);
    }
  }

  /*
   * Throws an error for this uncaught exception.
   */
  private void uncaughtExceptionError(QuercusLanguageException e)
  {
    Location location = e.getLocation(this);
    String type = e.getValue().getClassName();
    String message = e.getMessage(this);
   
    error(location,
          L.l("Uncaught exception of type '{0}' with message '{1}'", type, message));
  }

  /**
   * Executes the given page
   */
  protected Value executePage(QuercusPage page)
  {
    if (page.getCompiledPage() != null)
      return page.getCompiledPage().execute(this);
    else
      return page.execute(this);
  }

  /**
   * Executes the given page
   */
  protected Value executePageTop(QuercusPage page)
  {
    if (page.getCompiledPage() != null)
      return page.getCompiledPage().execute(this);
    else
      return page.execute(this);
  }
 
  /**
   * Evaluates the named function.
   *
   * @param name the function name
   * @return the function value
   */
  public Value call(String name)
  {
    AbstractFunction fun = findFunction(name);

    if (fun == null)
      return error(L.l("'{0}' is an unknown function.", name));

    return fun.call(this);
  }

  //
  // function calls (obsolete?)
  //

  /**
   * Evaluates the named function.
   *
   * @param name the function name
   * @param a0 the first argument
   * @return the function value
   */
  public Value call(String name, Value a0)
  {
    AbstractFunction fun = findFunction(name);

    if (fun == null)
      return error(L.l("'{0}' is an unknown function.", name));

    return fun.call(this, a0);
  }

  /**
   * Evaluates the named function.
   *
   * @param name the function name
   * @param a0 the first argument
   * @param a1 the second argument
   * @return the function value
   */
  public Value call(String name, Value a0, Value a1)
  {
    return getFunction(name).call(this, a0, a1);
  }

  /**
   * Evaluates the named function.
   *
   * @param name the function name
   * @param a0 the first argument
   * @param a1 the second argument
   * @param a2 the third argument
   * @return the function value
   */
  public Value call(String name, Value a0, Value a1, Value a2)
  {
    return getFunction(name).call(this, a0, a1, a2);
  }

  /**
   * Evaluates the named function.
   *
   * @param name the function name
   * @param a0 the first argument
   * @param a1 the second argument
   * @param a2 the third argument
   * @param a3 the fourth argument
   * @return the function value
   */
  public Value call(String name, Value a0, Value a1, Value a2, Value a3)
  {
    return getFunction(name).call(this, a0, a1, a2, a3);
  }

  /**
   * Evaluates the named function.
   *
   * @param name the function name
   * @param a0 the first argument
   * @param a1 the second argument
   * @param a2 the third argument
   * @param a3 the fourth argument
   * @param a4 the fifth argument
   * @return the function value
   */
  public Value call(String name, Value a0, Value a1,
                    Value a2, Value a3, Value a4)
  {
    return getFunction(name).call(this, a0, a1, a2, a3, a4);
  }

  /**
   * Evaluates the named function.
   *
   * @param name the function name
   * @param args the arguments
   * @return the function value
   */
  public Value call(String name, Value []args)
  {
    return getFunction(name).call(this, args);
  }

  /**
   * Evaluates the named function.
   *
   * @param name the function name
   * @return the function value
   */
  public Value callRef(String name)
  {
    AbstractFunction fun = findFunction(name);

    if (fun == null)
      return error(L.l("'{0}' is an unknown function.", name));

    return fun.callRef(this);
  }

  /**
   * EvalRefuates the named function.
   *
   * @param name the function name
   * @param a0 the first argument
   * @return the function value
   */
  public Value callRef(String name, Value a0)
  {
    AbstractFunction fun = findFunction(name);

    if (fun == null)
      return error(L.l("'{0}' is an unknown function.", name));

    return fun.callRef(this, a0);
  }

  /**
   * EvalRefuates the named function.
   *
   * @param name the function name
   * @param a0 the first argument
   * @param a1 the second argument
   * @return the function value
   */
  public Value callRef(String name, Value a0, Value a1)
  {
    AbstractFunction fun = findFunction(name);

    if (fun == null)
      return error(L.l("'{0}' is an unknown function.", name));

    return fun.callRef(this, a0, a1);
  }

  /**
   * EvalRefuates the named function.
   *
   * @param name the function name
   * @param a0 the first argument
   * @param a1 the second argument
   * @param a2 the third argument
   * @return the function value
   */
  public Value callRef(String name, Value a0, Value a1, Value a2)
  {
    AbstractFunction fun = findFunction(name);

    if (fun == null)
      return error(L.l("'{0}' is an unknown function.", name));

    return fun.callRef(this, a0, a1, a2);
  }

  /**
   * Evaluates the named function.
   *
   * @param name the function name
   * @param a0 the first argument
   * @param a1 the second argument
   * @param a2 the third argument
   * @param a3 the fourth argument
   * @return the function value
   */
  public Value callRef(String name, Value a0, Value a1, Value a2, Value a3)
  {
    AbstractFunction fun = findFunction(name);

    if (fun == null)
      return error(L.l("'{0}' is an unknown function.", name));

    return fun.callRef(this, a0, a1, a2, a3);
  }

  /**
   * Evaluates the named function.
   *
   * @param name the function name
   * @param a0 the first argument
   * @param a1 the second argument
   * @param a2 the third argument
   * @param a3 the fourth argument
   * @param a4 the fifth argument
   * @return the function value
   */
  public Value callRef(String name, Value a0, Value a1,
                       Value a2, Value a3, Value a4)
  {
    AbstractFunction fun = findFunction(name);

    if (fun == null)
      return error(L.l("'{0}' is an unknown function.", name));

    return fun.callRef(this, a0, a1, a2, a3, a4);
  }

  /**
   * Evaluates the named function.
   *
   * @param name the function name
   * @param args the arguments
   * @return the function value
   */
  public Value callRef(String name, Value []args)
  {
    AbstractFunction fun = findFunction(name);

    if (fun == null)
      return error(L.l("'{0}' is an unknown function.", name));

    return fun.callRef(this, args);
  }

  /**
   * Adds a class, e.g. from an include.
   */
  public void addClassDef(String name, ClassDef cl)
  {
    int id = _quercus.getClassId(name);

    if (_classDef.length <= id) {
      ClassDef []def = new ClassDef[id + 256];
      System.arraycopy(_classDef, 0, def, 0, _classDef.length);
      _classDef = def;
    }
   
    if (_classDef[id] == null)
      _classDef[id] = cl;
  }

  public ClassDef findClassDef(String name)
  {
    int id = _quercus.getClassId(name);

    if (id < _classDef.length)
      return _classDef[id];
    else
      return null;
  }

  /**
   * Saves the current state
   */
  public SaveState saveState()
  {
    if (_globalMap != _map)
      throw new QuercusException(L.l("Env.saveState() only allowed at top level"));
   
    return new SaveState(this,
                         _fun,
                         _classDef,
                         _qClass,
                         _const,
                         _globalMap,
                         _includeMap,
                         _importMap);
  }

  EnvVar []getGlobalList()
  {
    return _globalList;
  }

  /**
   * Returns true for any special variables, i.e. which should not be
   * saved
   */
  boolean isSpecialVar(String name)
  {
    if (Quercus.isSuperGlobal(name))
      return true;
    else if (_scriptGlobalMap.get(name) != null)
      return true;

    return false;
  }

  /**
   * Restores to a given state
   */
  public void restoreState(SaveState saveState)
  {
    AbstractFunction []fun = saveState.getFunctionList();
    if (_fun.length < fun.length)
      _fun = new AbstractFunction[fun.length];
   
    System.arraycopy(fun, 0, _fun, 0, fun.length);
   
    ClassDef []classDef = saveState.getClassDefList();
    if (_classDef.length < classDef.length)
      _classDef = new ClassDef[classDef.length];
     
    System.arraycopy(classDef, 0, _classDef, 0, classDef.length);
   
    QuercusClass []qClass = saveState.getQuercusClassList();
    if (_qClass.length < qClass.length)
      _qClass = new QuercusClass[qClass.length];
   
    System.arraycopy(qClass, 0, _qClass, 0, qClass.length);
   
    Value []constList = saveState.getConstantList();
    if (_const.length < constList.length)
      _const = new Value[constList.length];
   
    System.arraycopy(constList, 0, _const, 0, constList.length);

    IntMap globalNameMap = saveState.getGlobalNameMap();
    Value []globalList = saveState.getGlobalList();

    Map<String,EnvVar> oldGlobal = _globalMap;

    _globalMap = new LazySymbolMap(globalNameMap, globalList);
    _map = _globalMap;

    // php/4045 - set the vars for any active EnvVar entries
    for (Map.Entry<String,EnvVar> oldEntry : oldGlobal.entrySet()) {
      EnvVar oldEnvVar = oldEntry.getValue();

      EnvVar newEnvVar = _globalMap.get(oldEntry.getKey());

      if (newEnvVar != null)
        oldEnvVar.setRef(newEnvVar.getRef());
    }
   
    // php/404j - include_once
    HashMap<Path,QuercusPage> includeMap = saveState.getIncludeMap();
    _includeMap = new HashMap<Path,QuercusPage>(includeMap);
   
    // php/404l
    // XXX: import and namespaces
    _importMap = saveState.getImportMap().copy();
  }

  /**
   * Creates a stdClass object.
   */
  public ObjectValue createObject()
  {
    try {
      return (ObjectValue) _quercus.getStdClass().createObject(this);
    }
    catch (Exception e) {
      throw new QuercusModuleException(e);
    }
  }
 
  /**
   * Creates a stdClass object.
   */
  public ObjectValue createIncompleteObject(String name)
  {
    try {
      ObjectValue obj
        = (ObjectValue) _quercus.getStdClass().createObject(this);
     
      obj.setIncompleteObjectName(name);
     
      return obj;
     
    }
    catch (Exception e) {
      throw new QuercusModuleException(e);
    }
  }

  /*
   * Creates an empty string.
   */
  public StringValue getEmptyString()
  {
    if (_isUnicodeSemantics)
      return UnicodeBuilderValue.EMPTY;
    else
      return ConstStringValue.EMPTY;
  }

  /*
   * Creates an empty string builder.
   */
  public StringValue createStringBuilder()
  {
    if (_isUnicodeSemantics)
      return new UnicodeBuilderValue();
    else
      return new StringBuilderValue();
  }
 
  /**
   * Creates a PHP string from a byte buffer.
   */
  public StringValue createString(byte []buffer, int offset, int length)
  {
    if (_isUnicodeSemantics)
      return new UnicodeValueImpl(new String(buffer, offset, length));
    else
      return new ConstStringValue(buffer, offset, length);
  }
 
  /**
   * Creates a PHP string from a byte buffer.
   */
  public StringValue createString(char []buffer, int length)
  {
    if (_isUnicodeSemantics)
      return new UnicodeBuilderValue(buffer, length);
    else
      return new ConstStringValue(buffer, length);
  }
 
  /**
   * Creates a PHP string from a char buffer.
   */
  public StringValue createString(char []buffer, int offset, int length)
  {
    if (_isUnicodeSemantics)
      return new UnicodeBuilderValue(buffer, offset, length);
    else
      return new ConstStringValue(buffer, offset, length);
  }

  /**
   * Creates a PHP string from a java String.
   */
  public StringValue createString(String s, String encoding)
  {
    if (s == null || s.length() == 0) {
      return (_isUnicodeSemantics
              ? UnicodeBuilderValue.EMPTY
              : ConstStringValue.EMPTY);
    } else if (_isUnicodeSemantics) {
      if (s.length() == 1)
        return UnicodeBuilderValue.create(s.charAt(0));
      else
        return _quercus.createUnicodeString(s);
    }
    else {
      try {
        return new ConstStringValue(encoding == null ? s.getBytes(): s.getBytes(encoding));
      } catch (UnsupportedEncodingException e) {
        byte[] buffer = new byte[s.length()];
        for (int i = 0; i < buffer.length; ++i) {
          buffer[i] = (byte)(s.charAt(i) & 0xff);
        }
        return new ConstStringValue(buffer);
      }
    }
  }

  /**
   * Creates a string from a byte.
   */
  public StringValue createStringOld(char ch)
  {
    return createString(ch, getScriptEncoding());
  }

  public StringValue createStringOld(String str)
  {
    return createString(str, getScriptEncoding());
  }
 
  /**
   * Creates a string from a byte.
   */
  public StringValue createString(char ch, String encoding)
  {
    if (_isUnicodeSemantics)
      return UnicodeValueImpl.create(ch);
    else
      return ConstStringValue.create(ch, encoding == null ? getScriptEncoding(): null);
  }

  /**
   * Creates a PHP string from a buffer.
   */
  public StringValue createBinaryString(TempBuffer head)
  {
    StringValue string;
   
    if (_isUnicodeSemantics)
      string = new BinaryBuilderValue();
    else
      string = new StringBuilderValue();

    for (; head != null; head = head.getNext()) {
      string.append(head.getBuffer(), 0, head.getLength());
    }

    return string;
  }

  public Value createException(String exceptionClass, String message)
  {
    QuercusClass cls = getClass(exceptionClass);
   
    StringValue messageV = createStringOld(message);
    Value []args = { messageV };

    Value value = cls.callNew(this, args);
   
    Location location = getLocation();
   
    value.putField(this, "file", createStringOld(location.getFileName()));
    value.putField(this, "line", LongValue.create(location.getLineNumber()));
    value.putField(this, "trace", ErrorModule.debug_backtrace(this));

    return value;
  }
 
  /**
   * Creates a PHP Exception.
   */
  public Value createException(Throwable e)
  {
    QuercusClass cls = findClass("Exception");
   
    StringValue message = createStringOld(e.getMessage());
    Value []args = { message };

    Value value = cls.callNew(this, args);

    StackTraceElement elt = e.getStackTrace()[0];

    value.putField(this, "file", createStringOld(elt.getFileName()));
    value.putField(this, "line", LongValue.create(elt.getLineNumber()));
    value.putField(this, "trace", ErrorModule.debug_backtrace(this));

    return value;
  }
 
  /**
   * Generate an object id.
   */
  public int generateId()
  {
    return ++_objectId;
  }

  /**
   * Returns an introspected Java class definition.
   */
  public JavaClassDef getJavaClassDefinition(String className)
  {
    JavaClassDef def = getJavaClassDefinition(className, true);
   
    if (def != null)
      return def;
    else
      throw createErrorException(L.l("'{0}' class definition not found", className));
  }

  /*
   * Returns an introspected Java class definition.
   */
  public JavaClassDef getJavaClassDefinition(Class type)
  {
    JavaClassDef def = _quercus.getJavaClassDefinition(type, type.getName());
   
    return def;
  }
 
  private JavaClassDef getJavaClassDefinition(String className,
                                              boolean useImport)
  {
    JavaClassDef def = null;
   
    try {
      def = _quercus.getJavaClassDefinition(className);
     
      if (def == null && useImport) {
        useImport = false;
        def = importJavaClass(className);
      }
    }
    catch (Throwable e) {
      if (useImport)
        def = importJavaClass(className);
      else
        log.log(Level.FINER, e.toString(), e);
    }

    return def;
  }

  /**
   * Imports a Java class.
   *
   * @param className name of class to import
   * @return class definition of imported class, null if class not found
   */
  public JavaClassDef importJavaClass(String className)
  {
    if (_importMap == null)
      return null;
   
    String fullName = _importMap.getQualified(className);
   
    if (fullName != null) {
      return getJavaClassDefinition(fullName, false);
    }
    else {
      ArrayList<String> wildcardList
        = _importMap.getWildcardList();
 
      for (String entry : wildcardList) {
        fullName = entry + '.' + className;
         
        JavaClassDef def = getJavaClassDefinition(fullName, false);
       
        if (def != null) {
          _importMap.putQualified(className, fullName);
          return def;
        }
      }
    }

    return null;
  }

  /**
   * Adds a Quercus class import.
   *
   * @param javaName fully qualified class import string
   */
  public void putQualifiedImport(String javaName)
  {
    if (_importMap == null)
      _importMap = new ImportMap();
   
    _importMap.putQualified(javaName);
  }
 
  /**
   * Adds a Quercus class import.
   *
   * @param name wildcard class import string minus '*' at the end (i.e. java.util.)
   */
  public void addWildcardImport(String name)
  {
    if (_importMap == null)
      _importMap = new ImportMap();
   
    _importMap.addWildcardImport(name);
  }

  /**
   * Returns a PHP value for a Java object
   *
   * @param isNullAsFalse what to return if <i>obj</i> is null, if true return
   * {@link BooleanValue.FALSE} otherwise return {@link NullValue.NULL)
   */
  public Value wrapJava(Object obj, boolean isNullAsFalse)
  {
    if (obj == null) {
      if (isNullAsFalse)
        return BooleanValue.FALSE;
      else
        return NullValue.NULL;
    }

    return wrapJava(obj);
  }

  /**
   * Returns a PHP value for a Java object
   *
   * @param isNullAsFalse what to return if <i>obj</i> is null, if true return
   * {@link BooleanValue.FALSE} otherwise return {@link NullValue.NULL)
   */
  public <T> Value wrapJava(T obj, JavaClassDef<? extends T> def, boolean isNullAsFalse)
  {
    if (obj == null) {
      if (isNullAsFalse)
        return BooleanValue.FALSE;
      else
        return NullValue.NULL;
    }

    return wrapJava(obj, def);
  }

  /**
   * Returns a PHP value for a Java object
   */
  @SuppressWarnings("unchecked")
  public <T> Value wrapJava(T obj)
  {
    if (obj == null)
      return NullValue.NULL;

    if (obj instanceof Value)
      return (Value) obj;
   
    JavaClassDef<T> def = (JavaClassDef<T>)getJavaClassDefinition(obj.getClass());

    return def.wrap(this, obj);
  }

  /**
   * Returns a PHP value for a Java object
   *
   * @param isNullAsFalse what to return if <i>obj</i> is null, if true return
   * {@link BooleanValue.FALSE} otherwise return {@link NullValue.NULL)
   */
  @SuppressWarnings("unchecked")
  public <T> Value wrapJava(T obj, JavaClassDef<? extends T> def)
  {
    if (obj == null)
      return NullValue.NULL;

    if (obj instanceof Value)
      return (Value) obj;

    // XXX: why is this logic here?  The def should be correct on the call
    // logic is for JavaMarshal, where can avoid the lookup call
    if (def.getType() != obj.getClass()) {
      // XXX: what if types are incompatible, does it matter?
      // if it doesn't matter, simplify this to one if with no else
      def = (JavaClassDef<T>)getJavaClassDefinition(obj.getClass());
    }

    return ((JavaClassDef<T>)def).wrap(this, obj);
  }

  /**
   * Finds the class with the given name.
   *
   * @param name the class name
   * @return the found class or null if no class found.
   */
  public QuercusClass findClass(String name)
  {
    return findClass(name, true, true);
  }

  /**
   * Finds the class with the given name.
   *
   * @param name the class name
   * @param useAutoload use autoload to locate the class if necessary
   * @return the found class or null if no class found.
   */
  public QuercusClass findClass(String name,
                                boolean useAutoload,
                                boolean useImport)
  {
    int id = _quercus.getClassId(name);

    return findClass(id, useAutoload, useImport);
  }

  public QuercusClass findClass(int id,
                                boolean useAutoload,
                                boolean useImport)
  {
    if (id < _qClass.length && _qClass[id] != null)
      return _qClass[id];
   
    QuercusClass cl = createClassFromCache(id, useAutoload, useImport);
   
    if (cl != null) {
      _qClass[id] = cl;

      // php/09b7
      cl.init(this);

      return cl;
    }
    else {
      String name = _quercus.getClassName(id);

      QuercusClass qcl = findClassExt(name, useAutoload, useImport);

      if (qcl != null)
        _qClass[id] = qcl;
      else
        return null;

      return qcl;
    }
  }
 
  private QuercusClass findClassExt(String name,
                                    boolean useAutoload,
                                    boolean useImport)
  {
    int id = _quercus.getClassId(name);
   
    if (useAutoload) {
      StringValue nameString = createStringOld(name);
     
      if (! _autoloadClasses.contains(name)) {
        try {
          _autoloadClasses.add(name);

          int size = _autoloadList != null ? _autoloadList.size() : 0;

          for (int i = 0; i < size; i++) {
            Callback cb = _autoloadList.get(i);

            cb.call(this, nameString);
           
            // php/0977
            QuercusClass cls = findClass(name, false, useImport);
           
            if (cls != null)
              return cls;
          }

          if (size == 0) {
            if (_autoload == null)
              _autoload = findFunction("__autoload");
           
            if (_autoload != null) {
              _autoload.call(this, nameString);

              // php/0976
              QuercusClass cls = findClass(name, false, useImport);
           
              if (cls != null)
                return cls;
            }
          }
        } finally {
          _autoloadClasses.remove(name);
        }
      }
    }
   
    if (useImport) {
      if (importPhpClass(name)) {
        return findClass(name, false, false);
      }
      else {
        try {
          JavaClassDef javaClassDef = getJavaClassDefinition(name, true);

          if (javaClassDef != null) {
            QuercusClass cls = createQuercusClass(id, javaClassDef, null);

            _qClass[id] = cls;
           
            cls.init(this);
           
            return cls;
          }
        }
        catch (Exception e) {
          log.log(Level.FINER, e.toString(), e);
        }
      }
    }

    return _internalAutoload.loadClass(this, name);
  }

  /**
   * Returns the class with the given id
   */
  public QuercusClass getClass(int classId)
  {
    if (_qClass.length <= classId) {
      QuercusClass []oldClassList = _qClass;

      _qClass = new QuercusClass[classId + 256];
     
      System.arraycopy(oldClassList, 0, _qClass, 0, oldClassList.length);
    }
   
    QuercusClass qClass = _qClass[classId];

    if (qClass != null)
      return qClass;
   
    if (_classDef.length <= classId) {
      ClassDef []oldClassDefList = _classDef;

      _classDef = new ClassDef[classId + 256];
     
      System.arraycopy(oldClassDefList, 0,
                       _classDef, 0,
                       oldClassDefList.length);
    }

    ClassDef def = _classDef[classId];

    if (def == null) {
      QuercusClass cl = findClass(classId, true, true);
     
      if (cl != null)
        return cl;
      else
        throw new QuercusException(L.l("'{0}' is an unknown class",
                                       _quercus.getClassName(classId)));
    }

    int parentId = -1;

    if (def.getParentName() != null)
      parentId = _quercus.getClassId(def.getParentName());
   
    addClass(def, classId, parentId);
   
    return _qClass[classId];
  }

  /**
   * Adds the class with the given name
   *
   * @param def the class definition
   * @param classId the identifier for the class name
   * @param parentId the identifier for the parent class name
   */
  public void addClass(ClassDef def, int classId, int parentId)
  {
    def = def.loadClassDef();
   
    // php/0cn2 - make sure interfaces have a QuercusClass
    /* XXX: temp, needs to be argument
    for (String iface : def.getInterfaces()) {
      QuercusClass cl = findClass(iface);
    }
    */

    QuercusClass parentClass = null;

    if (parentId >= 0)
      parentClass = getClass(parentId);

    QuercusClass qClass = _quercus.getCachedClass(classId);

    if (qClass == null
        || qClass.isModified()
        || qClass.getClassDef() != def
        || qClass.getParent() != parentClass) {
      qClass = createQuercusClass(classId, def, parentClass);

      _quercus.setCachedClass(classId, qClass);
    }
   
    if (_qClass.length <= classId) {
      QuercusClass []oldClassList = _qClass;

      _qClass = new QuercusClass[classId + 256];
     
      System.arraycopy(oldClassList, 0, _qClass, 0, oldClassList.length);
    }

    _qClass[classId] = qClass;
    qClass.init(this);
  }

  public void addClass(String name, ClassDef def)
  {
    int id = _quercus.getClassId(name);

    int parentId = -1;
   
    if (def.getParentName() != null)
      parentId = _quercus.getClassId(def.getParentName());

    addClass(def, id, parentId);
  }

  /**
   * Finds the class with the given name.
   *
   * @param name the class name
   * @param useAutoload use autoload to locate the class if necessary
   * @param useImport import the class if necessary
   *
   * @return the found class or null if no class found.
   */
  private QuercusClass createClassFromCache(int id,
                                            boolean useAutoload,
                                            boolean useImport)
  {
    if (id < _classDef.length && _classDef[id] != null) {
      ClassDef classDef = _classDef[id];
     
      String parentName = classDef.getParentName();

      QuercusClass parent = null;

      if (parentName != null)
        parent = findClass(parentName);

      if (parentName == null || parent != null)
        return createQuercusClass(id, classDef, parent);
      else
        return null; // php/
    }

    ClassDef staticClass = _quercus.getClassDef(id);

    if (staticClass != null)
      return createQuercusClass(id, staticClass, null); // XXX: cache
    else
      return null;
  }
 
  /*
   * Registers an SPL autoload function.
   */
  public void addAutoloadFunction(Callback fun)
  {
    if (fun == null)
      throw new NullPointerException();
   
    if (_autoloadList == null)
      _autoloadList = new ArrayList<Callback>();

    _autoloadList.add(fun);
  }
 
  /*
   * Unregisters an SPL autoload function.
   */
  public void removeAutoloadFunction(Callback fun)
  {
    if (_autoloadList != null) {
      _autoloadList.remove(fun);
     
      //restore original __autoload functionality
      if (_autoloadList.size() == 0)
        _autoloadList = null;
    }
  }
 
  /*
   * Returns the registered SPL autoload functions.
   */
  public ArrayList<Callback> getAutoloadFunctions()
  {
    return _autoloadList;
  }

  /**
   * Imports a PHP class.
   *
   * @param name of the PHP class
   *
   * @return true if matching php file was found and included.
   */
  public boolean importPhpClass(String name)
  {
    if (_importMap == null)
      return false;
   
    String fullName = _importMap.getQualifiedPhp(name);
   
    URL url = null;
    ClassLoader loader = Thread.currentThread().getContextClassLoader();
   
    if (fullName != null) {
      url = loader.getResource(fullName);
    }
    else {
      for (String entry : _importMap.getWildcardPhpList()) {

        url = loader.getResource(entry + '/' + name + ".php");
       
        if (url != null)
          break;
      }
    }

    if (url != null) {
      includeOnce(new StringBuilderValue(url.toString()));
      return true;
    }
    else {
      return false;
    }
  }

  /**
   * Returns the declared classes.
   *
   * @return an array of the declared classes()
   */
  public Value getDeclaredClasses()
  {
    ArrayList<String> list = new ArrayList<String>();

    for (int i = 0; i < _classDef.length; i++) {
      if (_classDef[i] != null)
        list.add(_classDef[i].getName());
    }
   
    HashMap<String, ClassDef> classMap = getModuleContext().getClassMap();
   
    for (Map.Entry<String, ClassDef> entry : classMap.entrySet()) {
      list.add(entry.getKey());
    }

    Collections.sort(list);
   
    ArrayValue array = new ArrayValueImpl();
   
    Iterator<String> iter = list.iterator();
    while (iter.hasNext()) {
      array.put(iter.next());
    }
   
    return array;
  }

  /**
   * Finds the class with the given name.
   *
   * @param name the class name
   * @return the found class or null if no class found.
   */
  public QuercusClass findAbstractClass(String name)
  {
    QuercusClass cl = findClass(name, true, true);

    if (cl != null)
      return cl;

    throw createErrorException(L.l("'{0}' is an unknown class name.", name));
    /*
    // return _quercus.findJavaClassWrapper(name);

    return null;
    */
  }

  /**
   * Finds the class with the given name.
   *
   * @param name the class name
   * @return the found class
   * @throws QuercusRuntimeException if the class is not found
   */
  public QuercusClass getClass(String name)
  {
    QuercusClass cl = findClass(name);

    if (cl != null)
      return cl;
    else
      throw createErrorException(L.l("'{0}' is an unknown class.", name));
  }

  public void clearClassCache()
  {
    // _classCache.clear();
  }

  QuercusClass createJavaQuercusClass(JavaClassDef def)
  {
    int id = getQuercus().getClassId(def.getName());
   
    if (_qClass.length <= id) {
      QuercusClass []oldClassList = _qClass;
     
      _qClass = new QuercusClass[id + 256];
     
      System.arraycopy(oldClassList, 0, _qClass, 0, oldClassList.length);
    }

    if (_qClass[id] == null)
      _qClass[id] = def.getQuercusClass();

    return _qClass[id];
  }

  QuercusClass createQuercusClass(int id,
                                  ClassDef def,
                                  QuercusClass parent)
  {
    QuercusClass qClass = _quercus.getCachedClass(id);

    // php/0ac0
    if (_qClass.length <= id) {
      QuercusClass []oldClassList = _qClass;
     
      _qClass = new QuercusClass[id + 256];
     
      System.arraycopy(oldClassList, 0, _qClass, 0, oldClassList.length);
    }
   
    if (qClass == null
        || qClass.isModified()
        || qClass.getClassDef() != def
        || qClass.getParent() != parent) {
      qClass = new QuercusClass(getModuleContext(), def, parent);
      _qClass[id] = qClass;
     
      qClass.validate(this);
      _quercus.setCachedClass(id, qClass);
    }

    _qClass[id] = qClass;

    return qClass;
  }

  /**
   * Returns true if class has already been initialized.
   */
  public boolean isInitializedClass(String name)
  {
    return _initializedClassSet.contains(name);
  }
 
  /**
   * Mark this class as being initialized.
   */
  public void addInitializedClass(String name)
  {
    _initializedClassSet.add(name);
  }

  /**
   * Finds the class and method.
   *
   * @param className the class name
   * @param methodName the method name
   * @return the found method or null if no method found.
   */
  public AbstractFunction findFunction(String className, String methodName)
  {
    QuercusClass cl = findClass(className);

    if (cl == null)
      throw new QuercusRuntimeException(L.l("'{0}' is an unknown class",
                                            className));

    return cl.findFunction(methodName);
  }

  /**
   * Returns the appropriate callback.
   */
  public Callback createCallback(Value value)
  {
    if (value == null || value.isNull())
      return null;

    value = value.toValue();
 
    if (value instanceof Callback) {
      return (Callback) value;
    }
    else if (value instanceof StringValue) {
      // php/1h0o
      if (value.isEmpty())
        return null;

      String s = value.toString();

      int p = s.indexOf("::");

      if (p < 0)
        return new CallbackFunction(this, s);
      else {
        String className = s.substring(0, p);
        String methodName = s.substring(p + 2);
       
        QuercusClass cl = findClass(className);

        if (cl == null)
          throw new IllegalStateException(L.l("can't find class {0}",
                                              className));

        return new CallbackFunction(cl.getFunction(methodName));
      }
    }
    else if (value.isArray()) {
      Value obj = value.get(LongValue.ZERO);
      Value nameV = value.get(LongValue.ONE);
     
      if (! nameV.isString())
        throw new IllegalStateException(L.l("unknown callback name {0}", nameV));

      String name = nameV.toString();
     
      if (obj.isObject()) {
        AbstractFunction fun;
       
        int p = name.indexOf("::");
       
        // php/09lf
        if (p > 0) {
          String clsName = name.substring(0, p);
          name = name.substring(p + 2);

          QuercusClass cls = findClass(clsName);

          if (cls == null) {
            warning(L.l("Callback: '{0}' is not a valid callback class for {1}",
                        clsName, name));
         
            return null;
          }

          if (cls == null)
            throw new IllegalStateException(L.l("can't find class '{0}'",
                                                obj.toString()));

          fun = cls.getFunction(name);
        }
        else
          fun = obj.findFunction(name);
       
        return new CallbackObjectMethod(this, (ObjectValue) obj, fun, name);
      }
      else {
        QuercusClass cl = findClass(obj.toString());

        if (cl == null) {
          warning(L.l("Callback: '{0}' is not a valid callback string for {1}",
                      obj.toString(), obj));
         
          return null;
        }

        return new CallbackFunction(cl.getFunction(name));
      }
    }
    else
      return null;
  }

  /**
   * Evaluates an included file.
   */
  public Value requireOnce(StringValue include)
  {
    return include(getSelfDirectory(), include, true, true);
  }

  /**
   * Evaluates an included file.
   */
  public Value require(StringValue include)
  {
    return include(getSelfDirectory(), include, true, false);
  }

  /**
   * Evaluates an included file.
   */
  public Value include(StringValue include)
  {
    return include(getSelfDirectory(), include, false, false);
  }

  /**
   * Evaluates an included file.
   */
  public Value includeOnce(StringValue include)
  {
    return include(getSelfDirectory(), include, false, true);
  }

  /**
   * Evaluates an included file.
   */
  public Value includeOnce(Path scriptPwd, StringValue include,
                           boolean isRequire)
  {
    return include(scriptPwd, include, isRequire, true);
  }
 
  /**
   * Evaluates an included file.
   */
  public Value include(Path scriptPwd, StringValue include,
                       boolean isRequire, boolean isOnce)
  {
    try {
      Path pwd = getPwd();

      Path path = lookupInclude(include, pwd, scriptPwd);

      if (path != null) {
      }
      else if (isRequire) {
        error(L.l("'{0}' is not a valid path", include));
        return BooleanValue.FALSE;
      }
      else {
        warning(L.l("'{0}' is not a valid path", include));
        return BooleanValue.FALSE;
      }

      // php/0b2d
      if (! _isAllowUrlInclude && isUrl(path)) {
        String msg = (L.l("not allowed to include url {0}", path.getURL()));
       
        log.warning(dbgId() + msg);
        error(msg);

        return BooleanValue.FALSE;
      }

      QuercusPage page = _includeMap.get(path);
     
      if (page != null && isOnce)
        return BooleanValue.TRUE;
      else if (page == null || page.isModified()) {
        page = _quercus.parse(path);

        pageInit(page);
       
        _includeMap.put(path, page);
      }

      return executePage(page);
    } catch (IOException e) {
      throw new QuercusModuleException(e);
    }
  }
 
 

  void executePage(Path path)
  {
    try {
      QuercusPage page = _quercus.parse(path);

      pageInit(page);

      executePage(page);
    } catch (IOException e) {
      throw new QuercusException(e);
    }
  }
 
  /*
   * Returns true if this path is likely to be a URL.
   */
  private boolean isUrl(Path path)
  {
    String scheme = path.getScheme();
   
    if ("".equals(scheme)
        || "file".equals(scheme)
        || "memory".equals(scheme))
      return false;
   
    // XXX: too restrictive for filters
    return ! "php".equals(scheme)
           || "php://input".equals(path.toString())
           || path.toString().startsWith("php://filter");
  }

  /**
   * Looks up based on the pwd.
   */
  public Path lookupPwd(Value relPathV)
  {
    if (! relPathV.isset())
      return null;

    StringValue relPath = relPathV.toStringValue(this);

    if (relPath.length() == 0)
      return null;
   
    Path path = _lookupCache.get(relPath);

    if (path == null) {
      path = getPwd().lookup(normalizePath(relPath));
      _lookupCache.put(relPath, path);
    }

    return path;
  }

  /**
   * Looks up the path.
   */
  public Path lookup(StringValue relPath)
  {
    return lookupInclude(getSelfDirectory(), normalizePath(relPath));
  }

  /**
   * Looks up the path.
   */
  public Path lookupInclude(StringValue relPath)
  {
    return lookupInclude(relPath, getPwd(), getSelfDirectory());
  }

  private Path lookupInclude(StringValue include, Path pwd, Path scriptPwd)
  {
    String includePath = getDefaultIncludePath();
   
    Path path = _quercus.getIncludeCache(include, includePath, pwd, scriptPwd);

    if (path == null) {
      path = lookupIncludeImpl(include, pwd, scriptPwd);

      /*
      if (path == null)
        path = NullPath.NULL;
      */

      if (path != null)
        _quercus.putIncludeCache(include, includePath, pwd, scriptPwd, path);
    }

    if (path == NullPath.NULL)
      path = null;

    _includePath = includePath;
    _includePathIniCount = _iniCount;
   
    return path;
  }
 
  private String getDefaultIncludePath()
  {
    String includePath = _includePath;

    if (_includePathIniCount != _iniCount) {
      includePath = Quercus.INI_INCLUDE_PATH.getAsString(this);
      _includePath = null;
      _includePathList = null;
    }
   
    if (includePath == null)
      includePath = ".";
   
    return includePath;
  }
   
  private Path lookupIncludeImpl(StringValue includeValue,
                                 Path pwd,
                                 Path scriptPwd)
  {
    String include = normalizePath(includeValue);
   
    // php/0b0g

    Path path = lookupInclude(pwd, include);

    if (path == null) {
      // php/0b0l
      path = lookupInclude(scriptPwd, include);
    }

    if (path == null) {
      // php/0b21
      path = scriptPwd.lookup(include);

      if (! includeExists(path))
        path = null;
    }

    return path;
  }

  /**
   * Looks up the path.
   */
  private Path lookupInclude(Path pwd, String relPath)
  {
    ArrayList<Path> pathList = getIncludePath(pwd);

    for (int i = 0; i < pathList.size(); i++) {
      Path path = pathList.get(i).lookup(relPath);

      if (path.canRead() && ! path.isDirectory()) {
        return path;
      }
    }

    return null;
  }

  private boolean includeExists(Path path)
  {
    if (path.canRead() && ! path.isDirectory())
      return true;
    else if (! getQuercus().isRequireSource())
      return getQuercus().includeExists(path);
    else
      return false;
  }
  /**
   * Returns the include path.
   */
  private ArrayList<Path> getIncludePath(Path pwd)
  {
    String includePath = getDefaultIncludePath();

    if (_includePathList == null) {
      _includePathList = new ArrayList<String>();
      _includePathMap = new HashMap<Path,ArrayList<Path>>();

      int head = 0;
      int tail;

      String pathSeparator = FileModule.PATH_SEPARATOR;
      int length = pathSeparator.length();

      while ((tail = includePath.indexOf(pathSeparator, head)) >= 0) {
        String subpath = includePath.substring(head, tail);
       
        _includePathList.add(normalizePath(subpath));

        head = tail + length;
      }

      String subpath = includePath.substring(head);

      _includePathList.add(normalizePath(subpath));

      _includePath = includePath;
      _includePathIniCount = _iniCount;
    }

    ArrayList<Path> pathList = _includePathMap.get(pwd);

    if (pathList == null) {
      pathList = new ArrayList<Path>();

      for (int i = 0; i < _includePathList.size(); i++) {
        pathList.add(pwd.lookup(_includePathList.get(i)));
      }

      _includePathMap.put(pwd, pathList);
    }

    return pathList;
  }

  /**
   * Sets the include path.
   */
  public String setIncludePath(String path)
  {
    String prevIncludePath = Quercus.INI_INCLUDE_PATH.getAsString(this);

    if (_defaultIncludePath == null)
      _defaultIncludePath = prevIncludePath;

    Quercus.INI_INCLUDE_PATH.set(this, path);
   
    // reset include path cache count
    _includePathIniCount = -1;

    return prevIncludePath;
  }
 
  public String normalizePath(CharSequence path)
  {
    if (Path.isWindows()) {
      _cb.setLength(0);
     
      int len = path.length();
     
      if (len >= 3) {
        char ch;
        char ch3;
       
        if (path.charAt(1) == ':'
            && ('a' <= (ch = path.charAt(0)) && ch <= 'z'
                || 'A' <= ch && ch <= 'Z')
            && (ch3 = path.charAt(2)) != '/' && ch3 != '\\') {
          _cb.append(ch);
          _cb.append(':');
          _cb.append('\\');
          _cb.append(ch3);
         
          for (int i = 3; i < len; i++) {
            _cb.append(path.charAt(i));
          }
         
          return _cb.toString();
        }
      }
    }
   
    return path.toString();
     
     
      /*
     
     
     
     
      if (len >= 3) {
        char ch = path.charAt(0);
        char ch2 = path.charAt(1);
        char ch3 = path.charAt(2);
       
        _cb.append(ch);
        _cb.append(ch2);
        offset += 2;
       
        if (ch == '/') {
          if (('a' <= ch2 && ch2 <= 'z' || 'A' <= ch2 && ch2 <= 'Z')
              && ch3 == ':') {
            _cb.append(ch3);
            offset += 1;
           
            char ch4;
           
            if (len >= 4 && (ch4 = path.charAt(3)) != '/' && ch4 != '\\') {
              _cb.append('/');
              _cb.append(ch4);
              offset += 1;
            }
          }
          else if (ch2 == ':' && ch3 != '/' && ch3 != '\\') {
            _cb.append('/');
            _cb.append(ch3);
            offset += 1;
          }
        }
       
        if (ch == '/'
            && ('a' <= ch2 && ch2 <= 'z' || 'A' <= ch2 && ch2 <= 'Z')
            && ch3 == ':') {
         
          _cb.append(ch3);
          offset += 1;
         
          char ch4;
         
          if (len >= 4
              && (ch4 = path.charAt(3)) != '/' && ch4 != '\\') {
            _cb.append('/');
            _cb.append(ch4);
            offset += 1;
          }
        }
        else if (('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z')
                 && ch2 == ':'
                 && ch3 != '/' && ch3 != '\\') {
          _cb.append('/');
          _cb.append(ch3);
          offset += 1;
        }
      }
     
      for (; offset < len; offset++) {
        _cb.append(path.charAt(offset));
      }
     
      return _cb.toString();
     
    }
    else
      return path.toString();
   
    */
  }

  /**
   * Restores the default include path.
   */
  public void restoreIncludePath()
  {
    Quercus.INI_INCLUDE_PATH.set(this, _defaultIncludePath);
  }
 
  /**
   * Returns all the included files.
   */
  public ArrayValue getIncludedFiles()
  {
    ArrayValue array = new ArrayValueImpl();

    ArrayList<String> list = new ArrayList<String>();

    for (Path path : _includeMap.keySet()) {
      list.add(path.toString());
    }
     
    Collections.sort(list);
   
    for (String pathName : list) {
      array.put(createString(pathName, null));
    }
   
    return array;
  }

  /**
   * Handles error suppression.
   */
  public Value suppress(int errorMask, Value value)
  {
    setErrorMask(errorMask);

    return value;
  }

  /**
   * Handles exit/die
   */
  public Value exit(Value msg)
  {
    if (msg.isNull() || msg instanceof LongValue)
      return exit();

    try {
      getOut().print(msg.toString());
    } catch (IOException e) {
      log.log(Level.WARNING, e.toString(), e);
    }

    throw new QuercusExitException(msg.toString());
  }

  /**
   * Handles exit/die
   */
  public Value exit()
  {
    throw new QuercusExitException();
  }

  /**
   * Handles exit/die
   */
  public Value die(String msg)
  {
    try {
      getOut().print(msg);
    } catch (IOException e) {
      log.log(Level.WARNING, e.toString(), e);
    }

    throw new QuercusDieException(msg);
  }

  /**
   * Handles exit/die
   */
  public Value die()
  {
    throw new QuercusDieException();
  }

  /**
   * Handles exit/die
   */
  public Value cast(Class<?> cl, Value value)
  {
    value = value.toValue();

    if (value.isNull())
      return null;
    else if (cl.isAssignableFrom(value.getClass()))
      return value;
    else {
      // php/3cr2
      warning(L.l("{0} ({1}) is not assignable to {2}",
                value, value.getClass().getName(), cl.getName()));

      return null;
    }
  }

  /**
   * Returns the first value
   */
  public static Value first(Value value)
  {
    return value;
  }

  /**
   * Returns the first value
   */
  public static Value first(Value value, Value a1)
  {
    return value;
  }

  /**
   * Returns the first value
   */
  public static Value first(Value value, double a1)
  {
    return value;
  }

  /**
   * Returns the first value
   */
  public static long first(long value, Value a1)
  {
    return value;
  }

  /**
   * Returns the first value
   */
  public static double first(double value, Value a1)
  {
    return value;
  }

  /**
   * Returns the first value
   */
  public static long first(long value, double a1)
  {
    return value;
  }

  /**
   * Returns the first value
   */
  public static double first(double value, double a1)
  {
    return value;
  }

  /**
   * Returns the first value
   */
  public static Value first(Value value, Value a1, Value a2)
  {
    return value;
  }

  /**
   * Returns the first value
   */
  public static Value first(Value value, Value a1, Value a2, Value a3)
  {
    return value;
  }

  /**
   * Returns the first value
   */
  public static Value first(Value value, Value a1, Value a2, Value a3,
                            Value a4)
  {
    return value;
  }

  /**
   * Returns the first value
   */
  public static Value first(Value value, Value a1, Value a2, Value a3,
                            Value a4, Value a5)
  {
    return value;
  }

  /**
   * Check for type hinting
   */
  public void checkTypeHint(Value value,
                            String type,
                            String argName,
                            String functionName)
  {
    if (value.isNull()) {
      error(L.l("'{0}' is an unexpected value for arg '{1}' in function '{2}', expected '{3}'",
                value, argName, functionName, type));
    }
  }
     
  /**
   * A fatal runtime error.
   */
  public Value error(String msg)
  {
    return error(B_ERROR, "", msg + getFunctionLocation());
  }

  /**
   * A fatal runtime error.
   */
  public Value error(Location location, String msg)
  {
    return error(B_ERROR, location, msg + getFunctionLocation());
  }
 
  /**
   * A fatal runtime error.
   */
  public Value error(String loc, String msg)
  {
    return error(B_ERROR, loc, msg + getFunctionLocation());
  }

  /**
   * A warning with an exception.
   */
  public Value error(String msg, Throwable e)
  {
    log.log(Level.WARNING, e.toString(), e);

    return error(msg);
  }

  /**
   * A warning with an exception.
   */
  public Value error(Throwable e)
  {
    log.log(Level.WARNING, e.toString(), e);

    return error(e.toString());
  }

  /**
   * A fatal runtime error.
   */
  public QuercusRuntimeException createErrorException(String msg)
    throws QuercusRuntimeException
  {
    return createErrorException(null, msg);
  }

  /**
   * A fatal runtime error.
   */
  public QuercusRuntimeException createErrorException(Location location, String msg)
    throws QuercusRuntimeException
  {
    if (location == null)
      location = getLocation();

    location.getMessagePrefix();

    String fullMsg = msg + getFunctionLocation();

    error(B_ERROR, location, fullMsg);

    return new QuercusRuntimeException(fullMsg);
  }

  /**
   * A runtime warning.
   */
  public Value warning(String msg)
  {
    int mask = 1 << B_WARNING;
   
    if ((_errorMask & mask) != 0) {
      if (log.isLoggable(Level.FINER)) {
        QuercusException e = new QuercusException(msg);

        log.log(Level.FINER, e.toString(), e);
      }
    }

    return error(B_WARNING, "", msg + getFunctionLocation());
  }
 
  /**
   * A runtime warning.
   */
  public Value warning(Location location, String msg)
  {
    int mask = 1 << B_WARNING;
   
    if ((_errorMask & mask) != 0) {
      if (log.isLoggable(Level.FINER)) {
        QuercusException e = new QuercusException(msg);

        log.log(Level.FINER, e.toString(), e);
      }
    }

    return error(B_WARNING, location, "", msg + getFunctionLocation());
  }

  /**
   * A warning with an exception.
   */
  public Value warning(String msg, Throwable e)
  {
    log.log(Level.FINE, e.toString(), e);

    return warning(msg);
  }
 
  /**
   * A warning with an exception.
   */
  public Value warning(Location location, String msg, Throwable e)
  {
    log.log(Level.FINE, e.toString(), e);

    return warning(location, msg);
  }

  /**
   * A warning with an exception.
   */
  public Value warning(Throwable e)
  {
    return warning(e.toString(), e);
  }
 
  /**
   * A warning with an exception.
   */
  public Value warning(Location location, Throwable e)
  {
    return warning(location, e.toString(), e);
  }
 
  /**
   * A runtime strict warning.
   */
  public Value strict(String msg)
  {
    if (log.isLoggable(Level.FINER)) {
      QuercusException e = new QuercusException(msg);

      log.log(Level.FINER, e.toString(), e);
    }

    return error(B_STRICT, "", msg + getFunctionLocation());
  }

  /**
   * A warning about an invalid argument passed to a function.
   */
  public Value invalidArgument(String name, Object value)
  {
    return warning(L.l("invalid value `{0}' for `{1}'", value, name));
  }

  /**
   * A warning about an deprecated argument passed to a function.
   */
  public Value deprecatedArgument(String name)
  {
    return strict(L.l("argument `{1}' is deprecated", name));
  }

  /**
   * A notice.
   */
  public Value notice(String msg)
  {
    return error(B_NOTICE, "", msg + getFunctionLocation());
  }

  /**
   * A notice with an exception.
   */
  public Value notice(String msg, Throwable e)
  {
    log.log(Level.FINE, e.toString(), e);

    return notice(msg);
  }

  /**
   * A stub notice.
   */
  public Value stub(String msg)
  {
    if (log.isLoggable(Level.FINE))
      log.fine(getLocation().getMessagePrefix() + msg);

    return NullValue.NULL;
  }

  public static Value nullAsFalse(Value value)
  {
    return value == null || value.isNull() ? BooleanValue.FALSE : value;
  }

  /**
   * A parse error
   */
  public Value parse(String msg)
    throws Exception
  {
    return error(B_PARSE, "", msg);
  }

  /**
   * A parse error
   */
  public Value compileError(String msg)
  {
    return error(B_COMPILE_ERROR, "", msg);
  }

  /**
   * A parse warning
   */
  public Value compileWarning(String msg)
  {
    return error(B_COMPILE_WARNING, "", msg);
  }

  /**
   * Returns the error mask.
   */
  public int getErrorMask()
  {
    return _errorMask;
  }

  /**
   * Sets the error mask.
   */
  public int setErrorMask(int mask)
  {
    int oldMask = _errorMask;

    _errorMask = mask;

    return oldMask;
  }

  /**
   * Sets an error handler
   */
  public void setErrorHandler(int mask, Callback fun)
  {
    for (int i = 0; i < _errorHandlers.length; i++)
      _prevErrorHandlers[i] = _errorHandlers[i];

    if ((mask & E_ERROR) != 0)
      _errorHandlers[B_ERROR] = fun;

    if ((mask & E_WARNING) != 0)
      _errorHandlers[B_WARNING] = fun;

    if ((mask & E_PARSE) != 0)
      _errorHandlers[B_PARSE] = fun;

    if ((mask & E_NOTICE) != 0)
      _errorHandlers[B_NOTICE] = fun;

    if ((mask & E_USER_ERROR) != 0)
      _errorHandlers[B_USER_ERROR] = fun;

    if ((mask & E_USER_WARNING) != 0)
      _errorHandlers[B_USER_WARNING] = fun;

    if ((mask & E_USER_NOTICE) != 0)
      _errorHandlers[B_USER_NOTICE] = fun;

    if ((mask & E_STRICT) != 0)
      _errorHandlers[B_STRICT] = fun;
   
    if ((mask & E_RECOVERABLE_ERROR) != 0)
      _errorHandlers[B_RECOVERABLE_ERROR] = fun;
  }

  /**
   * Sets an error handler
   */
  public void restoreErrorHandler()
  {
    for (int i = 0; i < _errorHandlers.length; i++)
      _errorHandlers[i] = _prevErrorHandlers[i];
  }
 
  /**
   * Gets the exception handler
   */
  public Callback getExceptionHandler()
  {
    return _exceptionHandler;
  }
 
  /**
   * Sets an exception handler
   */
  public Value setExceptionHandler(Callback fun)
  {
    _prevExceptionHandler = _exceptionHandler;
   
    _exceptionHandler = fun;

    if (_prevExceptionHandler != null)
      return _prevExceptionHandler.toStringValue(this);
    else
      return NullValue.NULL;
  }
 
  /**
   * Restore an exception handler
   */
  public void restoreExceptionHandler()
  {
    _exceptionHandler = _prevExceptionHandler;
  }

  /*
   * Writes an error.
   */
  public Value error(int code, String locString, String msg)
  {
    return error(code, null, locString, msg);
  }
 
  /*
   * Writes an error.
   */
  public Value error(int code, Location location, String msg)
  {
    return error(code, location, "", msg);
  }
 
  /**
   * Writes an error.
   */
  public Value error(int code, Location location, String loc, String msg)
  {
    int mask = 1 << code;

    if (log.isLoggable(Level.FINEST)) {
      QuercusException e = new QuercusException(loc + msg);

      log.log(Level.FINEST, e.toString(), e);
    }
   
    if ((_errorMask & mask) != 0) {
      if (log.isLoggable(Level.FINE))
        log.fine(this + " " + loc + msg);
    }

    if (code >= 0 && code < _errorHandlers.length
        && _errorHandlers[code] != null) {
      Callback handler = _errorHandlers[code];

      try {
        _errorHandlers[code] = null;

        Value fileNameV = NullValue.NULL;

        if (location == null)
          location = getLocation();

        String fileName = location.getFileName();

        if (fileName != null)
          fileNameV = createString(fileName, null);

        Value lineV = NullValue.NULL;
        int line = location.getLineNumber();
        if (line > 0)
          lineV = LongValue.create(line);

        Value context = NullValue.NULL;

        handler.call(this, LongValue.create(mask), createStringOld(msg),
                     fileNameV, lineV, context);

        return NullValue.NULL;
      }
      catch (RuntimeException e) {
        throw e;
      }
      catch (Throwable e) {
        throw new RuntimeException(e);
      }
      finally {
        _errorHandlers[code] = handler;
      }
    }

    if ((_errorMask & mask) != 0) {
      try {
        String fullMsg = (getLocationPrefix(location, loc)
                          + getCodeName(mask) + msg);

        if (getIniBoolean("track_errors"))
          setGlobalValue("php_errormsg", createStringOld(fullMsg));
       
        if ("stderr".equals(getIniString("display_errors")))
          System.err.println(fullMsg);
        else if (getIniBoolean("display_errors"))
          getOut().println(fullMsg);

        if (getIniBoolean("log_errors"))
          log.info(fullMsg);
      }
      catch (IOException e) {
        log.log(Level.FINE, e.toString(), e);
      }
    }

    if ((mask & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR)) != 0)
    {
      String locPrefix = getLocationPrefix(location, loc);
     
      if (! "".equals(locPrefix)) {
        /*
        throw new QuercusLineExitException(getLocation() +
                                              getCodeName(mask) +
                                              msg);
        */
        throw new QuercusErrorException(locPrefix
                                        + getCodeName(mask)
                                        + msg);
      }
      else
        throw new QuercusErrorException(msg);
    }

    return NullValue.NULL;
  }

  /**
   * Returns the displayable location prefix.  This may be slow
   * for compiled-mode because of the need to match line numbers.
   */
  private String getLocationPrefix(Location location, String loc)
  {
    if (loc != null && ! "".equals(loc))
      return loc;
   
    if (location == null)
      location = getLocation();

    return location.getMessagePrefix();
  }

  /**
   * Returns the error code name.
   */
  private String getCodeName(int code)
  {
    switch (code) {
    case E_ERROR:
      return "Fatal Error: ";
    case E_WARNING:
      return "Warning: ";
    case E_PARSE:
      return "Parse Error: ";
    case E_NOTICE:
      return "Notice: ";
    case E_CORE_ERROR:
      return "Fatal Error: ";
    case E_CORE_WARNING:
      return "Warning: ";
    case E_COMPILE_ERROR:
      return "Fatal Error: ";
    case E_COMPILE_WARNING:
      return "Warning : ";
    case E_USER_ERROR:
      return "Fatal Error: ";
    case E_USER_WARNING:
      return "Warning: ";
    case E_USER_NOTICE:
      return "Notice: ";
    case E_STRICT:
      return "Notice: ";
    case E_RECOVERABLE_ERROR:
      return "Error: ";

    default:
      return String.valueOf("ErrorCode(" + code + ")");
    }
  }

  /**
   * Returns the source of an error line.
   */
  public static String []getSourceLine(Path path, int sourceLine, int length)
  {
    if (path == null)
      return null;
    else if (path instanceof NullPath) {
      // for QuercusScriptEngine.eval() where only a Reader is passed in
      // XXX: not too pretty
      return null;
    }

    ReadStream is = null;

    try {
      is = path.openRead();
      int ch;
      boolean hasCr = false;
      int line = 1;

      while (line < sourceLine) {
        ch = is.read();

        if (ch < 0)
          return null;
        else if (ch == '\r') {
          hasCr = true;
          line++;
        }
        else if (ch == '\n') {
          if (! hasCr)
            line++;
          hasCr = false;
        }
        else
          hasCr = false;
      }

      String []result = new String[length];

      int i = 0;
      StringBuilder sb = new StringBuilder();
      while (i < length && (ch = is.read()) > 0) {
        if (ch == '\n' && hasCr) {
          hasCr = false;
          continue;
        }
        else if (ch == '\r') {
          hasCr = true;
          result[i++] = sb.toString();
          sb.setLength(0);
        }
        else if (ch == '\n') {
          hasCr = false;
          result[i++] = sb.toString();
          sb.setLength(0);
        }
        else {
          hasCr = false;
          sb.append((char) ch);
        }
      }

      if (i < length)
        result[i] = sb.toString();

      return result;
    }
    catch (IOException e) {
      log.log(Level.FINE, e.toString(), e);
    }
    finally {
      if (is != null)
        is.close();
    }

    return null;
  }

  /**
   * Returns the current execution location.
   *
   * Use with care, for compiled code this can be a relatively expensive
   * operation.
   */
  public Location getLocation()
  {
    Expr call = peekCall(0);

    if (call != null)
      return call.getLocation();

    return Location.UNKNOWN;
  }

  public int getSourceLine(String className, int javaLine)
  {
    return javaLine;
  }

  /**
   * Returns the current function.
   */
  public String getFunctionLocation()
  {
    // XXX: need to work with compiled code, too
    Expr call = peekCall(0);

    if (call != null)
      return call.getFunctionLocation();
    else
      return "";
  }

  /**
   * Converts a boolean to the boolean value
   */
  public static Value toValue(boolean value)
  {
    return value ? BooleanValue.TRUE : BooleanValue.FALSE;
  }

  /**
   * Converts a boolean to the boolean value
   */
  public static Value toValue(long value)
  {
    return LongValue.create(value);
  }

  /**
   * Converts to a variable
   */
  public static Var toVar(Value value)
  {
    if (value instanceof Var)
      return (Var) value;
    else if (value == null)
      return new Var();
    else
      return new Var(value);
  }

  /**
   * Sets a vield variable
   */
  public static Value setFieldVar(Value oldValue, Value value)
  {
    if (value instanceof Var)
      return value;
    else if (oldValue instanceof Var)
      return new Var(value);
    else
      return value;
  }

  /**
   * Sets a reference
   */
  public static Value setRef(Value oldValue, Value value)
  {
    // php/3243
    if (value instanceof Var)
      return value;
    /*
    else if (oldValue instanceof Var) {
      oldValue.set(value);

      return oldValue;
    }
    */
    else
      return new Var(value);
  }

  /**
   * Sets a reference
   */
  public static Var setEnvRef(Var oldVar, Value value)
  {
    // 3ab7
    // XXX: need better test case, since that one isn't allowed by php
   
    if (value instanceof Var)
      return (Var) value;
    else {
      oldVar.set(value);

      return oldVar;
    }
  }

  /**
   * Returns the last value.
   */
  public static Value comma(Value a0, Value a1)
  {
    return a1;
  }

  /**
   * Returns the last value.
   */
  public static Value comma(Value a0, Value a1, Value a2)
  {
    return a2;
  }

  /**
   * Returns the last value.
   */
  public static Value comma(Value a0, Value a1, Value a2, Value a3)
  {
    return a3;
  }

  /**
   * Returns the last value.
   */
  public static Value comma(Value a0, Value a1, Value a2, Value a3, Value a4)
  {
    return a4;
  }

  // long comma

  /**
   * Returns the last value.
   */
  public static long comma(Value a0, long a1)
  {
    return a1;
  }

  /**
   * Returns the last value.
   */
  public static long comma(long a0, long a1)
  {
    return a1;
  }

  /**
   * Returns the last value.
   */
  public static Value comma(long a0, Value a1)
  {
    return a1;
  }

  //
  // comma
  //

  /**
   * Returns the last value.
   */
  public static double comma(Value a0, double a1)
  {
    return a1;
  }

  /**
   * Returns the last value.
   */
  public static double comma(double a0, double a1)
  {
    return a1;
  }

  /**
   * Returns the last value.
   */
  public static Value comma(double a0, Value a1)
  {
    return a1;
  }

  public String toString()
  {
    return "Env[]";
  }

  /**
   * Returns ifNull if condition.isNull(), otherwise returns ifNotNull.
   */
  public Value ifNull(Value condition, Value ifNull, Value ifNotNull)
  {
    return condition.isNull() ? ifNull : ifNotNull;
  }

  /**
   * Returns the locale info.
   */
  public LocaleInfo getLocaleInfo()
  {
    if (_locale == null)
      _locale = new LocaleInfo();

    return _locale;
  }
 
  public long getMicroTime()
  {
    return System.currentTimeMillis() * 1000;
  }

  /**
   * Registers a shutdown function.
   */
  public void addShutdown(Callback callback, Value []args)
  {
    if (_shutdownList == null)
      _shutdownList = new ArrayList<Shutdown>();
   
    _shutdownList.add(new Shutdown(callback, args));
  }

  // XXX: hack until can clean up
  public void setGzStream(Object obj)
  {
    _gzStream = obj;
  }

  // XXX: hack until can clean up
  public Object getGzStream()
  {
    return _gzStream;
  }

  /**
   * Called when the Env is no longer needed.
   */
  public void close()
  {
    try {
      // php/1l0t
      // output buffers callbacks may throw an exception
      while (_outputBuffer != null) {
        popOutputBuffer();
      }
    }
    //catch (Exception e) {
      //throw new RuntimeException(e);
    //}
    finally {
      cleanup();
    }
  }

  private void cleanup()
  {
    // cleanup is in reverse order of creation

    if (_objCleanupList != null) {
      for (int i = _objCleanupList.size() - 1; i >= 0; i--) {
        ObjectExtValue objCleanup = _objCleanupList.get(i);
        try {
          if (objCleanup != null)
            objCleanup.cleanup(this);
        }
        catch (Throwable e) {
          log.log(Level.FINER, e.toString(), e);
        }
      }
    }
     
    if (_shutdownList != null) {
      for (int i = 0; i < _shutdownList.size(); i++) {
        try {
          _shutdownList.get(i).call(this);
        }
        catch (Throwable e) {
          log.log(Level.FINE, e.toString(), e);
        }
      }
    }

    try {
      sessionWriteClose();
    } catch (Throwable e) {
      log.log(Level.FINE, e.toString(), e);
    }

    if (_cleanupList != null) {
      ArrayList<EnvCleanup> cleanupList
        = new ArrayList<EnvCleanup>(_cleanupList);

      // cleanup is in reverse order of creation
      for (int i = cleanupList.size() - 1; i >= 0; i--) {
        EnvCleanup envCleanup = cleanupList.get(i);
        try {
          if (envCleanup != null)
            envCleanup.cleanup();
        }
        catch (Throwable e) {
          log.log(Level.FINER, e.toString(), e);
        }
      }
    }

    _threadEnv.set(_oldThreadEnv);

    for (int i = 0; _removePaths != null && i < _removePaths.size(); i++) {
      Path path = _removePaths.get(i);

      try {
        path.remove();
      }
      catch (IOException e) {
        log.log(Level.FINER, e.toString(), e);
      }
    }

    AbstractFunction []fun = _fun;
    _fun = null;
    if (fun != null) {
      boolean isUsed = false;

      if (_page.setRuntimeFunction(fun)) {
        isUsed = true;
      }

      for (QuercusPage page : _includeMap.values()) {
        if (page.setRuntimeFunction(fun)) {
          isUsed = true;
        }
      }

      if (! isUsed) {
        for (int i = fun.length - 1; i >= 0; i--) {
          fun[i] = null;
        }
     
        _freeFunList.free(fun);
      }
    }

    ClassDef []classDef = _classDef;
    _classDef = null;
    if (classDef != null) {
      // php/0b3b
      for (int i = 0; i < classDef.length; i++) {
        classDef[i] = null;
      }
     
      _freeClassDefList.free(classDef);
    }

    QuercusClass []qClass = _qClass;
    _qClass = null;
    if (qClass != null) {
      for (int i = 0; i < qClass.length; i++) {
        qClass[i] = null;
      }
     
      _freeClassList.free(qClass);
    }
   
    Value []consts = _const;
    _const = null;
    if (consts != null) {
      for (int i = 0; i < consts.length; i++) {
        consts[i] = null;
      }
     
      _freeConstList.free(consts);
    }

    if (_gmtDate != null)
      _freeGmtDateList.free(_gmtDate);

    if (_localDate != null)
      _freeLocalDateList.free(_localDate);
  }

  public void sessionWriteClose()
  {
    SessionArrayValue session = _session;

    _session = null;

    if (session != null) {
      SessionCallback callback = getSessionCallback();

      if (callback != null) {
        String value;
       
        // php/1k6e
        if (session.getSize() > 0)
          value = VariableModule.serialize(this, session.getArray());
        else
          value = "";

        callback.write(this, session.getId(), value);

        callback.close(this);
      }
      else {
        _quercus.saveSession(this, session);

        Value sessionCopy = session.copy(this);
       
        setGlobalValue("_SESSION", sessionCopy);
        setGlobalValue("HTTP_SESSION_VARS", sessionCopy);
      }
    }
  }

  public String dbgId()
  {
    return getClass().getSimpleName() + "[" + _selfPath + "] ";
  }
 
  static class FieldGetEntry {
    private final String _className;
    private final StringValue _fieldName;
   
    FieldGetEntry(String className, StringValue fieldName)
    {
      _className = className;
      _fieldName = fieldName;
    }
   
    public boolean equals(Object o)
    {
      if (! (o instanceof FieldGetEntry))
        return false;
     
      FieldGetEntry entry = (FieldGetEntry) o;
     
      return entry._className.equals(_className)
             && entry._fieldName.equals(_fieldName);
    }
  }

  static class ClassKey {
    private final WeakReference<ClassDef> _defRef;
    private final WeakReference<QuercusClass> _parentRef;

    private final int _hash;

    ClassKey(ClassDef def, QuercusClass parent)
    {
      _defRef = new WeakReference<ClassDef>(def);

      if (parent != null)
        _parentRef = new WeakReference<QuercusClass>(parent);
      else
        _parentRef = null;

      // hash needs to be precalculated so losing a weak references won't
      // change the result
     
      int hash = 37;

      if (def != null)
        hash = 65521 * hash + def.hashCode();

      if (parent != null)
        hash = 65521 * hash + parent.hashCode();

      _hash = hash;
    }

    public int hashCode()
    {
      return _hash;
    }

    public boolean equals(Object o)
    {
      ClassKey key = (ClassKey) o;

      ClassDef aDef = _defRef.get();
      ClassDef bDef = key._defRef.get();

      if (aDef != bDef)
        return false;

      if (_parentRef == key._parentRef)
        return true;
     
      else if (_parentRef == null || key._parentRef == null)
        return false;

      QuercusClass aParent = _parentRef.get();
      QuercusClass bParent = key._parentRef.get();

      return (aParent != null && aParent.equals(bParent));
    }

    @Override
    public String toString()
    {
      return (getClass().getSimpleName()
              + "[" + _defRef.get() + ","
              + (_parentRef != null ? _parentRef.get() : null) + "]");
    }
  }

  static {
    SPECIAL_VARS.put("GLOBALS", _GLOBAL);
    SPECIAL_VARS.put("_SERVER", _SERVER);
    SPECIAL_VARS.put("_GET", _GET);
    SPECIAL_VARS.put("_POST", _POST);
    SPECIAL_VARS.put("_FILES", _FILES);
    SPECIAL_VARS.put("_REQUEST", _REQUEST);
    SPECIAL_VARS.put("_COOKIE", _COOKIE);
    SPECIAL_VARS.put("_SESSION", _SESSION);
    SPECIAL_VARS.put("_ENV", _ENV);
    SPECIAL_VARS.put("HTTP_GET_VARS", HTTP_GET_VARS);
    SPECIAL_VARS.put("HTTP_POST_VARS", HTTP_POST_VARS);
    SPECIAL_VARS.put("HTTP_POST_FILES", HTTP_POST_FILES);
    SPECIAL_VARS.put("HTTP_COOKIE_VARS", HTTP_COOKIE_VARS);
    SPECIAL_VARS.put("HTTP_SERVER_VARS", HTTP_SERVER_VARS);
    SPECIAL_VARS.put("PHP_SELF", PHP_SELF);
    SPECIAL_VARS.put("HTTP_RAW_POST_DATA", HTTP_RAW_POST_DATA);
  }
}
TOP

Related Classes of com.caucho.quercus.env.Env$FieldGetEntry

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.