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