diff --git a/compile.sh b/compile.sh new file mode 100755 index 0000000..1910007 --- /dev/null +++ b/compile.sh @@ -0,0 +1 @@ +gradle build && cp -v quercus/build/libs/*.jar /Users/vista/.grails/1.3.7/projects/QuertcusUTF8test2/plugins/php-0.1.8/lib/ diff --git a/quercus/src/main/java/com/caucho/quercus/env/Env.java b/quercus/src/main/java/com/caucho/quercus/env/Env.java index a7fc3a1..fdc7358 100644 --- a/quercus/src/main/java/com/caucho/quercus/env/Env.java +++ b/quercus/src/main/java/com/caucho/quercus/env/Env.java @@ -73,6880 +73,6878 @@ */ 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_DEPRECATED = 13; - public static final int B_USER_DEPRECATED = 14; - public static final int B_LAST = B_USER_DEPRECATED; - 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 = 30719; // E_ERROR | E_WARNING | E_PARSE | E_NOTICE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE | E_RECOVERABLE_ERROR | E_DEPRECATED | E_USER_DEPRECATED - 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_DEPRECATED = 1 << B_DEPRECATED; - public static final int E_USER_DEPRECATED = 1 << B_USER_DEPRECATED; - public static final int E_DEFAULT = 30711; // 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"); - private static final StringValue S_GET = new ConstStringValue("_GET"); - private static final StringValue S_POST = new ConstStringValue("_POST"); - private static final StringValue S_SESSION = new ConstStringValue("_SESSION"); - private static final StringValue S_SERVER = new ConstStringValue("_SERVER"); - private static final StringValue S_COOKIE = new ConstStringValue("_COOKIE"); - private static final StringValue S_FILES = new ConstStringValue("_FILES"); - public static final Value[] EMPTY_VALUE = new Value[0]; - private static ThreadLocal _threadEnv = new ThreadLocal(); - private static final FreeList _freeFunList = new FreeList(256); - private static final FreeList _freeClassDefList = new FreeList(256); - private static final FreeList _freeClassList = new FreeList(256); - private static final FreeList _freeConstList = new FreeList(256); - private static final FreeList _freeGmtDateList = new FreeList(256); - private static final FreeList _freeLocalDateList = new FreeList(256); - private static final LruCache _internStringMap = new LruCache(4096); - protected final QuercusContext _quercus; - private QuercusPage _page; - private HashMap _scriptGlobalMap = new HashMap(16); - // Function map - public AbstractFunction[] _fun; - // anonymous functions created by create_function() - public HashMap _anonymousFunMap; - // Class map - public ClassDef[] _classDef; - public QuercusClass[] _qClass; - // Constant map - public Value[] _const; - // Globals - private Map _globalMap = new HashMap(); - private EnvVar[] _globalList; - // Statics - private Map _staticMap = new HashMap(); - // Current env - private Map _map = _globalMap; - private HashMap _iniMap; - // specialMap is used for implicit resources like the mysql link - private HashMap _specialMap = new HashMap(); - private HashSet _initializedClassSet = new HashSet(); - // include_path ini - private int _iniCount = 1; - private ArrayList _cleanupList; - private ArrayList _objCleanupList; - private ArrayList _shutdownList; - private String _defaultIncludePath; - private String _includePath; - private int _includePathIniCount; - private ArrayList _includePathList; - private HashMap> _includePathMap; - // TODO: may require a LinkedHashMap for ordering? - private HashMap _includeMap = new HashMap(); - private Value _this = NullThisValue.NULL; - private final boolean _isUnicodeSemantics; - private boolean _isAllowUrlInclude; - private boolean _isAllowUrlFopen; - private HashMap _lookupCache = new HashMap(); - private HashMap _connMap = new HashMap(); - private AbstractFunction _autoload; - private HashSet _autoloadClasses = new HashSet(); - private ArrayList _autoloadList; - private InternalAutoloadCallback _internalAutoload; - private Location _location; - 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 _fieldGetList = new LinkedList(); - private LinkedList _fieldSetList = new LinkedList(); - private LinkedList _issetList = new LinkedList(); - private LinkedList _unsetList = new LinkedList(); - - public enum OVERLOADING_TYPES { - - INVALID_FIRST, FIELDGET, FIELDSET, ISSET, UNSET, INVALID_LAST - }; - private Path _selfPath; - private Path _selfDirectory; - private Path _pwd; - private Path _uploadPath; - private Path _tmpPath; - private ArrayList _removePaths; - private final boolean _isStrict; - private HttpServletRequest _request; - private HttpServletResponse _response; - private ArrayValue _postArray = new ArrayValueImpl(); - private ArrayValue _files = new ArrayValueImpl(); - private StringValue _inputData; - private SessionArrayValue _session; - private HttpSession _javaSession; - private ScriptContext _scriptContext; - private WriteStream _originalOut; - private OutputBuffer _outputBuffer; - private WriteStream _out; - private LocaleInfo _locale; - private Callable[] _prevErrorHandlers = new Callback[B_LAST + 1]; - private Callable[] _errorHandlers = new Callback[B_LAST + 1]; - private Callable _prevExceptionHandler; - private Callable _exceptionHandler; - private SessionCallback _sessionCallback; - private StreamContextResource _defaultStreamContext; - 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 boolean _isTimeout; - private long _firstMicroTime; - private long _firstNanoTime; - private RegexpState _freeRegexpState; - private Object _duplex; - private StringValue _variablesOrder; - private int[] _querySeparatorMap; - public static final int[] DEFAULT_QUERY_SEPARATOR_MAP; - private CharBuffer _cb = new CharBuffer(); - private String[] _authRequest; - - public Env(QuercusContext quercus, - QuercusPage page, - WriteStream out, - HttpServletRequest request, - HttpServletResponse response) { - _quercus = quercus; - - _isStrict = quercus.isStrict(); - _isUnicodeSemantics = quercus.isUnicodeSemantics(); - - _isAllowUrlInclude = quercus.isAllowUrlInclude(); - _isAllowUrlFopen = quercus.isAllowUrlFopen(); - - _variablesOrder = _quercus.getIniValue("variables_order").toStringValue(this); - - StringValue querySeparators = _quercus.getIniValue("arg_separator.input").toStringValue(this); - - int len = querySeparators.length(); - if (len == 0) { - _querySeparatorMap = DEFAULT_QUERY_SEPARATOR_MAP; - } else { - _querySeparatorMap = new int[128]; - - for (int i = 0; i < len; i++) { - char ch = querySeparators.charAt(i); - - _querySeparatorMap[ch] = 1; - } - } - - _page = page; - - // TODO: 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); - } - - // TODO: delete internal Autoload Callback - _internalAutoload = new InternalAutoloadCallback("com/caucho/quercus/php/"); - - // Define the constant string PHP_VERSION - addConstant("PHP_VERSION", OptionsModule.phpversion(this, null), true); - - // Define the constant string PHP_SAPI - addConstant("PHP_SAPI", new ConstStringValue(OptionsModule.php_sapi_name(this)), true); - - addConstant("PEAR_EXTENSION_DIR", new ConstStringValue(getPwd() + "WEB-INF/lib/"), true); - addConstant("PHP_EXTENSION_DIR", new ConstStringValue(getPwd() + "WEB-INF/lib/"), true); - addConstant("PHP_LIBDIR", new ConstStringValue(getPwd() + "WEB-INF/lib/"), true); - addConstant("PHP_CONFIG_FILE_PATH", new ConstStringValue(getPwd() + "WEB-INF/"), true); - addConstant("PHP_CONFIG_FILE_SCAN_DIR", new ConstStringValue(getPwd() + "WEB-INF/"), true); + 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_DEPRECATED = 13; + public static final int B_USER_DEPRECATED = 14; + public static final int B_LAST = B_USER_DEPRECATED; + 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 = 30719; // E_ERROR | E_WARNING | E_PARSE | E_NOTICE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE | E_RECOVERABLE_ERROR | E_DEPRECATED | E_USER_DEPRECATED + 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_DEPRECATED = 1 << B_DEPRECATED; + public static final int E_USER_DEPRECATED = 1 << B_USER_DEPRECATED; + public static final int E_DEFAULT = 30711; // 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"); + private static final StringValue S_GET = new ConstStringValue("_GET"); + private static final StringValue S_POST = new ConstStringValue("_POST"); + private static final StringValue S_SESSION = new ConstStringValue("_SESSION"); + private static final StringValue S_SERVER = new ConstStringValue("_SERVER"); + private static final StringValue S_COOKIE = new ConstStringValue("_COOKIE"); + private static final StringValue S_FILES = new ConstStringValue("_FILES"); + public static final Value[] EMPTY_VALUE = new Value[0]; + private static ThreadLocal _threadEnv = new ThreadLocal(); + private static final FreeList _freeFunList = new FreeList(256); + private static final FreeList _freeClassDefList = new FreeList(256); + private static final FreeList _freeClassList = new FreeList(256); + private static final FreeList _freeConstList = new FreeList(256); + private static final FreeList _freeGmtDateList = new FreeList(256); + private static final FreeList _freeLocalDateList = new FreeList(256); + private static final LruCache _internStringMap = new LruCache(4096); + protected final QuercusContext _quercus; + private QuercusPage _page; + private HashMap _scriptGlobalMap = new HashMap(16); + // Function map + public AbstractFunction[] _fun; + // anonymous functions created by create_function() + public HashMap _anonymousFunMap; + // Class map + public ClassDef[] _classDef; + public QuercusClass[] _qClass; + // Constant map + public Value[] _const; + // Globals + private Map _globalMap = new HashMap(); + private EnvVar[] _globalList; + // Statics + private Map _staticMap = new HashMap(); + // Current env + private Map _map = _globalMap; + private HashMap _iniMap; + // specialMap is used for implicit resources like the mysql link + private HashMap _specialMap = new HashMap(); + private HashSet _initializedClassSet = new HashSet(); + // include_path ini + private int _iniCount = 1; + private ArrayList _cleanupList; + private ArrayList _objCleanupList; + private ArrayList _shutdownList; + private String _defaultIncludePath; + private String _includePath; + private int _includePathIniCount; + private ArrayList _includePathList; + private HashMap> _includePathMap; + // TODO: may require a LinkedHashMap for ordering? + private HashMap _includeMap = new HashMap(); + private Value _this = NullThisValue.NULL; + private final boolean _isUnicodeSemantics; + private boolean _isAllowUrlInclude; + private boolean _isAllowUrlFopen; + private HashMap _lookupCache = new HashMap(); + private HashMap _connMap = new HashMap(); + private AbstractFunction _autoload; + private HashSet _autoloadClasses = new HashSet(); + private ArrayList _autoloadList; + private InternalAutoloadCallback _internalAutoload; + private Location _location; + 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 _fieldGetList = new LinkedList(); + private LinkedList _fieldSetList = new LinkedList(); + private LinkedList _issetList = new LinkedList(); + private LinkedList _unsetList = new LinkedList(); + + public enum OVERLOADING_TYPES { + + INVALID_FIRST, FIELDGET, FIELDSET, ISSET, UNSET, INVALID_LAST + }; + private Path _selfPath; + private Path _selfDirectory; + private Path _pwd; + private Path _uploadPath; + private Path _tmpPath; + private ArrayList _removePaths; + private final boolean _isStrict; + private HttpServletRequest _request; + private HttpServletResponse _response; + private ArrayValue _postArray = new ArrayValueImpl(); + private ArrayValue _files = new ArrayValueImpl(); + private StringValue _inputData; + private SessionArrayValue _session; + private HttpSession _javaSession; + private ScriptContext _scriptContext; + private WriteStream _originalOut; + private OutputBuffer _outputBuffer; + private WriteStream _out; + private LocaleInfo _locale; + private Callable[] _prevErrorHandlers = new Callback[B_LAST + 1]; + private Callable[] _errorHandlers = new Callback[B_LAST + 1]; + private Callable _prevExceptionHandler; + private Callable _exceptionHandler; + private SessionCallback _sessionCallback; + private StreamContextResource _defaultStreamContext; + 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 boolean _isTimeout; + private long _firstMicroTime; + private long _firstNanoTime; + private RegexpState _freeRegexpState; + private Object _duplex; + private StringValue _variablesOrder; + private int[] _querySeparatorMap; + public static final int[] DEFAULT_QUERY_SEPARATOR_MAP; + private CharBuffer _cb = new CharBuffer(); + private String[] _authRequest; + + public Env(QuercusContext quercus, + QuercusPage page, + WriteStream out, + HttpServletRequest request, + HttpServletResponse response) { + _quercus = quercus; + + _isStrict = quercus.isStrict(); + _isUnicodeSemantics = quercus.isUnicodeSemantics(); + + _isAllowUrlInclude = quercus.isAllowUrlInclude(); + _isAllowUrlFopen = quercus.isAllowUrlFopen(); + + _variablesOrder = _quercus.getIniValue("variables_order").toStringValue(this); + + StringValue querySeparators = _quercus.getIniValue("arg_separator.input").toStringValue(this); + + int len = querySeparators.length(); + if (len == 0) { + _querySeparatorMap = DEFAULT_QUERY_SEPARATOR_MAP; + } else { + _querySeparatorMap = new int[128]; + + for (int i = 0; i < len; i++) { + char ch = querySeparators.charAt(i); + + _querySeparatorMap[ch] = 1; + } + } + + _page = page; + + // TODO: 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); + } + + // TODO: delete internal Autoload Callback + _internalAutoload = new InternalAutoloadCallback("com/caucho/quercus/php/"); + + // Define the constant string PHP_VERSION + addConstant("PHP_VERSION", OptionsModule.phpversion(this, null), true); + + // Define the constant string PHP_SAPI + addConstant("PHP_SAPI", new ConstStringValue(OptionsModule.php_sapi_name(this)), true); + + addConstant("PEAR_EXTENSION_DIR", new ConstStringValue(getPwd() + "WEB-INF/lib/"), true); + addConstant("PHP_EXTENSION_DIR", new ConstStringValue(getPwd() + "WEB-INF/lib/"), true); + addConstant("PHP_LIBDIR", new ConstStringValue(getPwd() + "WEB-INF/lib/"), true); + addConstant("PHP_CONFIG_FILE_PATH", new ConstStringValue(getPwd() + "WEB-INF/"), true); + addConstant("PHP_CONFIG_FILE_SCAN_DIR", new ConstStringValue(getPwd() + "WEB-INF/"), true); + + // c#0004403 - #27 + if (request != null) { + String _authHeader = request.getHeader("authorization"); + if (_authHeader != null) { + _authRequest = _authHeader.split(" "); + if (_authRequest[0].equals("Basic")) { + // BASIC auth + String[] _auth64 = Base64.decode(_authRequest[1]).split(":"); + getGlobalVar("_SERVER").put(new ConstStringValue("PHP_AUTH_USER"), new ConstStringValue(_auth64[0])); + getGlobalVar("_SERVER").put(new ConstStringValue("PHP_AUTH_PW"), new ConstStringValue(_auth64[1])); + } + } + } + + // 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(QuercusContext quercus) { + this(quercus, null, null, null, null); + } + + public static Env getCurrent() { + return _threadEnv.get(); + } + + public static Env getInstance() { + return getCurrent(); + } + + private void fillGet(ArrayValue array, boolean isMagicQuotes) { + String queryString = getQueryString(); + + if (queryString == null || queryString.length() == 0) { + return; + } + + StringUtility.parseStr(this, + queryString, + array, + true, + getHttpInputEncoding(), + isMagicQuotes, + _querySeparatorMap); + + /* + try { + String encoding = getHttpInputEncoding(); + + if (encoding == null) + encoding = "iso-8859-1"; + + _request.setCharacterEncoding(encoding); + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + } + + ArrayList keys = new ArrayList(); + keys.addAll(_request.getParameterMap().keySet()); + + Collections.sort(keys); + + for (String key : keys) { + String []value = _request.getParameterValues(key); + + Post.addFormValue(this, + array, + key, + value, + isMagicQuotes); + } + */ + } + + private void fillPost(ArrayValue array, ArrayValue postArray) { + for (Map.Entry 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()); + } + } + } + + private void fillCookies(ArrayValue array, + Cookie[] cookies, + boolean isMagicQuotes) { + for (int i = 0; cookies != null && i < cookies.length; i++) { + Cookie cookie = cookies[i]; - // c#0004403 - #27 - if (request != null) { - String _authHeader = request.getHeader("authorization"); - if (_authHeader != null) { - _authRequest = _authHeader.split(" "); - if (_authRequest[0].equals("Basic")) { - // BASIC auth - String[] _auth64 = Base64.decode(_authRequest[1]).split(":"); - getGlobalVar("_SERVER").put(new ConstStringValue("PHP_AUTH_USER"), new ConstStringValue(_auth64[0])); - getGlobalVar("_SERVER").put(new ConstStringValue("PHP_AUTH_PW"), new ConstStringValue(_auth64[1])); + String decodedValue = decodeValue(cookie.getValue()); + + Post.addFormValue(this, + array, + cookie.getName(), + new String[]{decodedValue}, + isMagicQuotes); + } + } + + protected void fillPost(ArrayValue postArray, + ArrayValue files, + HttpServletRequest request, + boolean isMagicQuotes) { + if (request != null && request.getMethod().equals("POST")) { + Post.fillPost(this, + postArray, + files, + request, + isMagicQuotes, + getIniBoolean("file_uploads")); + } else if (request != null && !request.getMethod().equals("GET")) { + InputStream is = null; + + try { + is = request.getInputStream(); + } catch (IOException e) { + warning(e); } - } - } - - // 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(QuercusContext quercus) { - this(quercus, null, null, null, null); - } - - public static Env getCurrent() { - return _threadEnv.get(); - } - - public static Env getInstance() { - return getCurrent(); - } - - private void fillGet(ArrayValue array, boolean isMagicQuotes) { - String queryString = getQueryString(); - - if (queryString == null || queryString.length() == 0) { - return; - } - - StringUtility.parseStr(this, - queryString, - array, - true, - getHttpInputEncoding(), - isMagicQuotes, - _querySeparatorMap); - - /* - try { - String encoding = getHttpInputEncoding(); - - if (encoding == null) - encoding = "iso-8859-1"; - - _request.setCharacterEncoding(encoding); - } catch (Exception e) { - log.log(Level.FINE, e.toString(), e); - } - - ArrayList keys = new ArrayList(); - keys.addAll(_request.getParameterMap().keySet()); - - Collections.sort(keys); - - for (String key : keys) { - String []value = _request.getParameterValues(key); - - Post.addFormValue(this, - array, - key, - value, - isMagicQuotes); - } - */ - } - - private void fillPost(ArrayValue array, ArrayValue postArray) { - for (Map.Entry 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()); - } - } - } - - private void fillCookies(ArrayValue array, - Cookie[] cookies, - boolean isMagicQuotes) { - 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); - } - } - - protected void fillPost(ArrayValue postArray, - ArrayValue files, - HttpServletRequest request, - boolean isMagicQuotes) { - if (request != null && request.getMethod().equals("POST")) { - Post.fillPost(this, - postArray, - files, - request, - isMagicQuotes, - getIniBoolean("file_uploads")); - } 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); - } - } - - 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 null; - } - - 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 = QuercusContext.INI_UNICODE_OUTPUT_ENCODING.getAsString(this); - - if (encoding == null) { - encoding = QuercusContext.INI_UNICODE_FALLBACK_ENCODING.getAsString(this); - } - - if (encoding == null) { - encoding = "utf-8"; - } - - return encoding; - } - - /** - * 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() { - if (_defaultTimeZone != null) { - return _defaultTimeZone; - } - - String timeZone = getIniString("date.timezone"); - - if (timeZone != null) { - return TimeZone.getTimeZone(timeZone); - } else { - return TimeZone.getDefault(); - } - } - - 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 QDate getDate() { - TimeZone zone = getDefaultTimeZone(); - - if (zone.getID().equals("GMT")) { - return getGmtDate(); - } else if (zone.equals(TimeZone.getDefault())) { - return getLocalDate(); - } else { - return new QDate(zone); - } - } - - 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 = _quercus.getCurrentTime(); - _timeLimit = getIniLong("max_execution_time") * 1000; - - if (_timeLimit > 0) { - _endTime = _startTime + _timeLimit; - } else { - _endTime = Long.MAX_VALUE / 2; - } - - _threadEnv.set(this); - - fillPost(_postArray, - _files, - _request, - getIniBoolean("magic_quotes_gpc")); - - // 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 listeners = _quercus.getModuleStartupListeners(); - - for (ModuleStartupListener listener : listeners) { - listener.startup(this); - } - - _quercus.startEnv(this); - } - - /** - * Returns the current time (may be cached). - */ - public long getCurrentTime() { - return getQuercus().getCurrentTime(); - } - - /** - * Returns the current time (not cached). - */ - public long getExactTime() { - return getQuercus().getExactTime(); - } - - /** - * 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(); - } - - _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(); - } - - _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 QuercusContext 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 { - // TODO: 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) { + + StringValue bb = createBinaryBuilder(); + bb.appendReadAll(is, Integer.MAX_VALUE); + + setInputData(bb); + } + } + + 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 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 void checkTimeout() { - /* - long now = Alarm.getCurrentTime(); - - if (_endTime < now) - throw new QuercusRuntimeException(L.l("script timed out")); - */ - if (_isTimeout) { - throw new QuercusRuntimeException(L.l("script timed out")); - } - } - - public void updateTimeout() { - long now = _quercus.getCurrentTime(); - - if (_endTime < now) { - _isTimeout = true; - } - } - - public void resetTimeout() { - _startTime = _quercus.getCurrentTime(); - _endTime = _startTime + _timeLimit; - _isTimeout = false; - } - - public long getStartTime() { - return _startTime; - } - - /** - * 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 { + } + + 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 = QuercusContext.INI_UNICODE_OUTPUT_ENCODING.getAsString(this); + + if (encoding == null) { + encoding = QuercusContext.INI_UNICODE_FALLBACK_ENCODING.getAsString(this); + } + + if (encoding == null) { + encoding = "utf-8"; + } + + return encoding; + } + + /** + * 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() { + if (_defaultTimeZone != null) { + return _defaultTimeZone; + } + + String timeZone = getIniString("date.timezone"); + + if (timeZone != null) { + return TimeZone.getTimeZone(timeZone); + } else { + return TimeZone.getDefault(); + } + } + + 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 QDate getDate() { + TimeZone zone = getDefaultTimeZone(); + + if (zone.getID().equals("GMT")) { + return getGmtDate(); + } else if (zone.equals(TimeZone.getDefault())) { + return getLocalDate(); + } else { + return new QDate(zone); + } + } + + 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 = _quercus.getCurrentTime(); + _timeLimit = getIniLong("max_execution_time") * 1000; + + if (_timeLimit > 0) { + _endTime = _startTime + _timeLimit; + } else { + _endTime = Long.MAX_VALUE / 2; + } + + _threadEnv.set(this); + + fillPost(_postArray, + _files, + _request, + getIniBoolean("magic_quotes_gpc")); + + // 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 listeners = _quercus.getModuleStartupListeners(); + + for (ModuleStartupListener listener : listeners) { + listener.startup(this); + } + + _quercus.startEnv(this); + } + + /** + * Returns the current time (may be cached). + */ + public long getCurrentTime() { + return getQuercus().getCurrentTime(); + } + + /** + * Returns the current time (not cached). + */ + public long getExactTime() { + return getQuercus().getExactTime(); + } + + /** + * 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(); + } + + _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(); + } + + _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 QuercusContext 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 { + // TODO: 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 void checkTimeout() { + /* + long now = Alarm.getCurrentTime(); + + if (_endTime < now) + throw new QuercusRuntimeException(L.l("script timed out")); + */ + if (_isTimeout) { + throw new QuercusRuntimeException(L.l("script timed out")); + } + } + + public void updateTimeout() { + long now = _quercus.getCurrentTime(); + + if (_endTime < now) { + _isTimeout = true; + } + } + + public void resetTimeout() { + _startTime = _quercus.getCurrentTime(); + _endTime = _startTime + _timeLimit; + _isTimeout = false; + } + + public long getStartTime() { + return _startTime; + } + + /** + * 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 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(Callable 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) { - } else if (getRequest() != null) { - realPath = "WEB-INF/upload"; - } else { - realPath = WorkDir.getTmpWorkDir().lookup("upload").getNativePath(); - } - - _uploadPath = _quercus.getPwd().lookup(realPath); - - try { - if (!_uploadPath.isDirectory()) { - _uploadPath.mkdirs(); + } 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) { - log.log(Level.FINE, e.toString(), e); - } - - _uploadPath = _uploadPath.createRoot(); - } - - return _uploadPath; - } - - /** - * Returns the real path. - */ - public String getRealPath(String path) { - String realPath; - - if (getRequest() != null) { - realPath = getRequest().getRealPath(path); - } else { - realPath = getSelfDirectory().lookup(path).getNativePath(); - } - - return realPath; - } - - /** - * Returns the temp directory (used by tmpfile()). - */ - public Path getTempDirectory() { - String realPath; - - if (_tmpPath == null) { - if (getRequest() != null) { - realPath = getRequest().getRealPath("/WEB-INF/tmp"); - } else { - realPath = "file:/tmp"; - } - - _tmpPath = getPwd().lookup(realPath); - - try { - if (!_tmpPath.isDirectory()) { - _tmpPath.mkdirs(); + } 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(Callable 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) { + } else if (getRequest() != null) { + realPath = "WEB-INF/upload"; + } else { + realPath = WorkDir.getTmpWorkDir().lookup("upload").getNativePath(); } - } 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(); - } - - _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(S_SESSION); - - if (!(var instanceof SessionVar)) { - var = new SessionVar(); - setGlobalValue(S_SESSION, var); - } - - var.set(session); - - setGlobalValue("HTTP_SESSION_VARS", session); - - session.addUse(); - } else { - // php/1k0v - Value v = getGlobalVar(S_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 = _quercus.getCurrentTime(); - - 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); + + _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 real path. + */ + public String getRealPath(String path) { + String realPath; + + if (getRequest() != null) { + realPath = getRequest().getRealPath(path); + } else { + realPath = getSelfDirectory().lookup(path).getNativePath(); + } + + return realPath; + } + + /** + * Returns the temp directory (used by tmpfile()). + */ + public Path getTempDirectory() { + String realPath; + + if (_tmpPath == null) { + if (getRequest() != null) { + realPath = getRequest().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(); + } + + _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(S_SESSION); + + if (!(var instanceof SessionVar)) { + var = new SessionVar(); + setGlobalValue(S_SESSION, var); + } + + var.set(session); + + setGlobalValue("HTTP_SESSION_VARS", session); + + session.addUse(); + } else { + // php/1k0v + Value v = getGlobalVar(S_SESSION); + + if (v != null) { + v.set(UnsetValue.UNSET); } - } - } - - 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 getIniMap(boolean create) { - if (_iniMap == null && create) { - _iniMap = new HashMap(); - } - - 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(StringValue name) { - return getValue(name, true, false); - } - - /** - * Gets a value. - */ - public Value getValue(StringValue name, - boolean isAutoCreate, - boolean isOutputNotice) { - EnvVar var = getEnvVar(name, isAutoCreate, isOutputNotice); - - if (var != null) { - return var.get(); - } 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); - } - - /** - * 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; - } - - public Value getGlobalValue(String name) { - return getGlobalValue(createString(name)); - } - - /** - * Gets a global - */ - public Value getGlobalValue(StringValue name) { - EnvVar var = getGlobalEnvVar(name); - - // TODO: 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(StringValue 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(StringValue name, Value value) { - if (value != null) { - return (Var) value; - } - - return getGlobalRef(name); - } - - /** - * Gets a value. - */ - public Var getRef(StringValue name) { - EnvVar envVar = getEnvVar(name); - - // TODO: can return null? - - return envVar.getVar(); - - /* - 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(StringValue name, boolean isAutoCreate) { - EnvVar envVar = getEnvVar(name, isAutoCreate, true); - - if (envVar != null) { - return envVar.getVar(); - } else { - return null; - } - } - - /** - * Returns the raw global lookup. - */ - public EnvVar getGlobalRaw(String name) { - return _globalMap.get(name); - } - - /** - * Gets a global value. - */ - public Var getGlobalRef(StringValue name) { - EnvVar envVar = getGlobalEnvVar(name); - - // TODO: not create? - - return envVar.getVar(); - } - - public final EnvVar getEnvVar(StringValue name) { - return getEnvVar(name, true, false); - } - - /** - * Gets a variable - * - * @param name the variable name - * @param var the current value of the variable - */ - public final EnvVar getEnvVar(StringValue name, - boolean isAutoCreate, - boolean isOutputNotice) { - EnvVar envVar = _map.get(name); - - if (envVar != null) { - return envVar; - } - - if (_map == _globalMap) { - return getGlobalEnvVar(name, isAutoCreate, isOutputNotice); - } - - envVar = getSuperGlobalRef(name, true, false); - - // php/0809 - if (envVar != null) { - _globalMap.put(name, envVar); - } else { - if (!isAutoCreate) { - // php/0206 - if (isOutputNotice) { - notice(L.l("${0} is an undefined variable", name)); + + 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 = _quercus.getCurrentTime(); + + 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 getIniMap(boolean create) { + if (_iniMap == null && create) { + _iniMap = new HashMap(); + } + + 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(StringValue name) { + return getValue(name, true, false); + } + + /** + * Gets a value. + */ + public Value getValue(StringValue name, + boolean isAutoCreate, + boolean isOutputNotice) { + EnvVar var = getEnvVar(name, isAutoCreate, isOutputNotice); + + if (var != null) { + return var.get(); + } 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); + } + + /** + * 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; + } + + public Value getGlobalValue(String name) { + return getGlobalValue(createString(name)); + } + + /** + * Gets a global + */ + public Value getGlobalValue(StringValue name) { + EnvVar var = getGlobalEnvVar(name); + + // TODO: 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(StringValue 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(StringValue name, Value value) { + if (value != null) { + return (Var) value; + } + + return getGlobalRef(name); + } + + /** + * Gets a value. + */ + public Var getRef(StringValue name) { + EnvVar envVar = getEnvVar(name); + + // TODO: can return null? + + return envVar.getVar(); + + /* + 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(StringValue name, boolean isAutoCreate) { + EnvVar envVar = getEnvVar(name, isAutoCreate, true); + + if (envVar != null) { + return envVar.getVar(); + } else { + return null; + } + } + + /** + * Returns the raw global lookup. + */ + public EnvVar getGlobalRaw(String name) { + return _globalMap.get(name); + } + + /** + * Gets a global value. + */ + public Var getGlobalRef(StringValue name) { + EnvVar envVar = getGlobalEnvVar(name); + + // TODO: not create? + + return envVar.getVar(); + } + + public final EnvVar getEnvVar(StringValue name) { + return getEnvVar(name, true, false); + } + + /** + * Gets a variable + * + * @param name the variable name + * @param var the current value of the variable + */ + public final EnvVar getEnvVar(StringValue name, + boolean isAutoCreate, + boolean isOutputNotice) { + EnvVar envVar = _map.get(name); + + if (envVar != null) { + return envVar; + } + + if (_map == _globalMap) { + return getGlobalEnvVar(name, isAutoCreate, isOutputNotice); + } + + envVar = getSuperGlobalRef(name, true, false); + + // php/0809 + if (envVar != null) { + _globalMap.put(name, envVar); + } else { + if (!isAutoCreate) { + // php/0206 + if (isOutputNotice) { + notice(L.l("${0} is an undefined variable", name)); + } + + 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(StringValue name) { + return getGlobalEnvVar(name, true, false); + } + + /** + * Gets a variable + * + * @param name the variable name + * @param isAutoCreate + */ + public final EnvVar getGlobalEnvVar(StringValue name, + boolean isAutoCreate, + boolean isOutputNotice) { + EnvVar envVar = _globalMap.get(name); + + if (envVar != null) { + return envVar; + } + + envVar = getSuperGlobalRef(name, true); + + 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) { + + if (isOutputNotice) { + notice(L.l("${0} is an undefined variable", name)); + } + + return null; + } + + Var var = new Var(); + // var.setGlobal(); + + envVar = new EnvVarImpl(var); + } + + _globalMap.put(name, envVar); + + return envVar; + } + + /** + * Pushes a new environment. + */ + public Map pushEnv(Map map) { + Map oldEnv = _map; + + _map = map; + + return oldEnv; + } + + /** + * Restores the old environment. + */ + public void popEnv(Map oldEnv) { + _map = oldEnv; + } + + /** + * Returns the current environment. + */ + public Map getEnv() { + return _map; + } + + /** + * Returns the current environment. + */ + public Map getGlobalEnv() { + return _globalMap; + } + + public boolean isGlobalEnv() { + return _map == _globalMap; + } + + /** + * Gets a static variable name. + */ + public final StringValue createStaticName() { + return _quercus.createStaticName(); + } + + /** + * Gets a static variable + * + * @param name the variable name + */ + public final Var getStaticVar(StringValue name) { + Var var = _staticMap.get(name); + + if (var == null) { + var = new Var(); + //System.err.println("getStaticVar(StringValue name) " + name); + _staticMap.put(name, var); + } + + return var; + } + + /** + * Gets a static variable + * + * @param name the variable name + */ + public final Value getStaticValue(StringValue name) { + //System.err.println("getStaticValue(StringValue name) " + name + " of type " + name.getClass().getName()); + //System.err.println("_staticMap " + _staticMap); + Var var = _staticMap.get(name); + //System.err.println("_staticMap.get(" + name + ") = " + var + " # " + _staticMap.get(name)); + if (var != null) { + return var.toValue(); + } else { + return NullValue.NULL; + } + } + + /** + * Gets a static variable + * + * @param name the variable name + */ + public final Var setStaticRef(StringValue name, Value value) { + if (value.isVar()) { + Var var = (Var) value; + //System.err.println("setStaticRef(StringValue name, Value value) " + var + " has " + var.hashCode()); + _staticMap.put(name, var); + + return var; + } + + Var var = _staticMap.get(name); + + if (var == null) { + var = new Var(); + //System.err.println("setStaticRef(StringValue name, Value value) => " + name + " hash " + name.hashCode() + " - " + name.getClass().getName() + " - " + value); + _staticMap.put(name, var); + } + + var.set(value); + + return var; + } + + /** + * Gets a static variable + * + * @param name the variable name + */ + /* + public final Var getStaticClassVar(Value qThis, + String className, + String name) + { + // php/3248 + // php/324a + // php/324b + QuercusClass callingClass = getCallingClass(qThis); + + if (callingClass.isA(className)) + className = callingClass.getName(); + + StringValue varName = createString(className).append("::").append(name); + + return getStaticVar(varName); + } + */ + /** + * Unsets variable + * + * @param name the variable name + */ + public final Var unsetVar(StringValue name) { + EnvVar envVar = _map.get(name); + + if (envVar != null) { + envVar.setVar(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(StringValue name) { + EnvVar envVar = _map.get(name); + + if (envVar != null) { + envVar.setVar(new Var()); + } + + return null; + } + + /** + * Unsets variable + * + * @param name the variable name + */ + public final Var unsetGlobalVar(StringValue name) { + EnvVar envVar = _globalMap.get(name); + + if (envVar != null) { + envVar.setVar(new Var()); + } + + return null; + } + + /** + * Gets a local + * + * @param var the current value of the variable + */ + public static 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 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 Value setLocalVar(Value var, Value value) { + value = value.toValue(); + + if (var instanceof Var) { + var.set(value); + } + + return value; + } + + private EnvVar getSuperGlobalRef(StringValue name, boolean isGlobal) { + return getSuperGlobalRef(name, false, isGlobal); + } + + /** + * Returns a superglobal. + */ + private EnvVar getSuperGlobalRef(StringValue name, + boolean isCheckGlobal, + boolean isGlobal) { + 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; + } + } + } + + 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 (!QuercusContext.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this)) { + return null; + } else { + return getGlobalEnvVar(S_POST); + } + + case _POST: { + var = new Var(); + envVar = new EnvVarImpl(var); + + _globalMap.put(name, envVar); + + ArrayValue post = new ArrayValueImpl(); + + envVar.set(post); + + if (_variablesOrder.indexOf('P') >= 0 + && _postArray.getSize() > 0) { + for (Map.Entry entry : _postArray.entrySet()) { + post.put(entry.getKey(), entry.getValue()); + } + } + + return envVar; + } + + case HTTP_POST_FILES: + if (!QuercusContext.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this)) { + return null; + } else { + return getGlobalEnvVar(S_FILES); + } + + case _FILES: { + var = new Var(); + envVar = new EnvVarImpl(var); + + _globalMap.put(name, envVar); + + ArrayValue files = new ArrayValueImpl(); + + if (_files != null) { + for (Map.Entry entry : _files.entrySet()) { + files.put(entry.getKey(), entry.getValue()); + } + } + + envVar.set(files); + + return envVar; + } + + case HTTP_GET_VARS: + if (!QuercusContext.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this)) { + return null; + } else if (!isGlobal) { + return null; + } else { + return getGlobalEnvVar(S_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 (_variablesOrder.indexOf('G') >= 0) { + fillGet(array, getIniBoolean("magic_quotes_gpc")); + } + + 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; + } + + boolean isMagicQuotes = getIniBoolean("magic_quotes_gpc"); + + int orderLen = _variablesOrder.length(); + + for (int i = 0; i < orderLen; i++) { + switch (_variablesOrder.charAt(i)) { + case 'G': + fillGet(array, isMagicQuotes); + break; + case 'P': + if (_postArray.getSize() > 0) { + fillPost(array, _postArray); + } + break; + case 'C': + fillCookies(array, _request.getCookies(), isMagicQuotes); + break; + } + } + + return envVar; + } + + case HTTP_RAW_POST_DATA: { + if (!QuercusContext.INI_ALWAYS_POPULATE_RAW_POST_DATA.getAsBoolean(this)) { + String contentType = getContentType(); + + if (contentType == null || !contentType.equals("unknown/type")) { + 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 (!QuercusContext.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this)) { + return null; + } else { + return getGlobalEnvVar(S_SERVER); + } + + case _SERVER: { + var = new Var(); + envVar = new EnvVarImpl(var); + + _globalMap.put(name, envVar); + + Value serverEnv; + + if (_variablesOrder.indexOf('S') >= 0) { + serverEnv = new ServerArrayValue(this); + + String query = getQueryString(); + + if (_quercus.getIniBoolean("register_argc_argv") + && query != null) { + ArrayValue argv = new ArrayValueImpl(); + + int i = 0; + int j = 0; + while ((j = query.indexOf('+', i)) >= 0) { + String sub = query.substring(i, j); + + argv.put(sub); + + i = j + 1; + } + + if (i < query.length()) { + argv.put(query.substring(i)); + } + + serverEnv.put(createString("argc"), + LongValue.create(argv.getSize())); + + serverEnv.put(createString("argv"), argv); + } + } else { + serverEnv = new ArrayValueImpl(); + } + + var.set(serverEnv); + + 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 (!QuercusContext.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this)) { + return null; + } else { + return getGlobalEnvVar(S_COOKIE); + } + + case _COOKIE: { + var = new Var(); + envVar = new EnvVarImpl(var); + + _globalMap.put(name, envVar); + + if (_variablesOrder.indexOf('C') >= 0) { + var.set(getCookies()); + } else { + var.set(new ArrayValueImpl()); + } + + 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; + } + } + + protected String getQueryString() { + if (_request != null) { + return _request.getQueryString(); + } else { + return null; + } + } + + protected String getContentType() { + if (_request != null) { + return _request.getContentType(); + } else { + return null; + } + } + + protected ArrayValue getCookies() { + ArrayValue array = new ArrayValueImpl(); + boolean isMagicQuotes = getIniBoolean("magic_quotes_gpc"); + + 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); + + if (isMagicQuotes) // php/0876 + { + valueAsValue = StringModule.addslashes(valueAsValue); + } + + array.append(createString(cookie.getName()), valueAsValue); + } + } + + return array; + } + + public void setArgs(String[] args) { + if (_quercus.getIniBoolean("register_argc_argv")) { + ArrayValue argv = new ArrayValueImpl(); + + for (String arg : args) { + argv.put(arg); + } + + Value serverEnv = getGlobalValue("_SERVER"); + + serverEnv.put(createString("argc"), + LongValue.create(args.length)); + + serverEnv.put(createString("argv"), argv); + } + } + + /** + * Gets a value. + */ + protected EnvVar getGlobalSpecialRef(StringValue name) { + if (QuercusContext.isSuperGlobal(name)) { + return _globalMap.get(name); + } else { + return null; + } + } + + protected EnvVar getGlobalScriptContextRef(StringValue name) { + if (_scriptContext == null) { + return null; + } + + EnvVar envVar = _globalMap.get(name); + + if (envVar != null) { + return envVar; + } + + Object value = _scriptContext.getAttribute(name.toString()); + + if (value == null) { + Bindings bindings = _scriptContext.getBindings(ScriptContext.ENGINE_SCOPE); + + if (bindings != null) { + value = bindings.get(name.toString()); + } + } + + if (value == null) { + Bindings bindings = _scriptContext.getBindings(ScriptContext.GLOBAL_SCOPE); + + if (bindings != null) { + value = bindings.get(name.toString()); + } + } + + if (value != null) { + envVar = new EnvVarImpl(new Var()); + + _globalMap.put(name, envVar); + + envVar.set(wrapJava(value)); + } + + return envVar; + } + + protected 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(); + } + + public Var getVar(String name) { + return getVar(createString(name)); + } + + /** + * Gets a value. + */ + public Var getVar(StringValue name) { + EnvVar envVar = getEnvVar(name); + + return envVar.getVar(); + } + + /** + * Gets a value. + */ + public Var getGlobalVar(String name) { + EnvVar envVar = getGlobalEnvVar(createString(name)); + + return envVar.getVar(); + } + + /** + * Gets a value. + */ + public Var getGlobalVar(StringValue name) { + EnvVar envVar = getGlobalEnvVar(name); + + return envVar.getVar(); + } + + public void setValue(String name, Value value) { + // TODO: update to Quercus + setValue(createString(name), value); + } + + /** + * Sets a value. value must not be a Var. + */ + public Value setValue(StringValue name, Value value) { + EnvVar envVar = getEnvVar(name); + + envVar.set(value); + + return value; + } + + /** + * Sets a variable. + */ + public Var setVar(StringValue name, Var var) { + EnvVar envVar = getEnvVar(name); + + envVar.setVar(var); + + return var; + } + + /** + * Sets a value. + */ + public Var setRef(StringValue name, Value value) { + EnvVar envVar = getEnvVar(name); + + return envVar.setRef(value); + } + + /** + * Sets a value. + */ + /* + public static Var toRef(Value value) + { + // php/3243 + + if (value instanceof Var) + return (Var) value; + else + return new Var(value); + } + */ + /** + * Convert to a reference argument. It's a PHP error to convert anything + * but a var or null to a ref arg. + */ + /* + public Value toRefArgument(Value value) + { + // php/33lg + + if (value instanceof Var) + return (Var) value; + else if (value instanceof NullValue) + return NullValue.NULL; + else { + warning(L.l("'{0}' is an invalid reference, " + + "because only variables may be passed by reference.", + value)); + + return NullValue.NULL; + } + } + */ + /** + * External calls to set a global value. + */ + public Value setGlobalValue(String name, Value value) { + return setGlobalValue(createString(name), value); + } + + /** + * External calls to set a global value. + */ + public Value setGlobalValue(StringValue name, Value value) { + EnvVar envVar = getGlobalEnvVar(name); + + envVar.setRef(value); + + return value; + } + + /** + * 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 name doesn't already exist on the + * field __get() stack. + */ + public boolean pushFieldGet(Env.OVERLOADING_TYPES type, String className, StringValue fieldName) { + FieldGetEntry entry = new FieldGetEntry(className, fieldName); + + LinkedList list = null; + switch (type) { + case FIELDGET: + list = _fieldGetList; + break; + case FIELDSET: + list = _fieldSetList; + break; + case ISSET: + list = _issetList; + break; + case UNSET: + list = _unsetList; + break; + case INVALID_FIRST: + case INVALID_LAST: + // defensive programming according to "Code Complete 2nd Edition, MS Press" + throw new IllegalStateException("IllegalState: pushFieldGet with FIRST/LAST Element"); + } + + if (list == null) { + return false; + } + + if (list.contains(entry)) { + return false; + } else { + list.push(entry); + + return true; + } + } + + public void popFieldGet(Env.OVERLOADING_TYPES type) { + switch (type) { + case FIELDGET: + _fieldGetList.pop(); + break; + case FIELDSET: + _fieldSetList.pop(); + break; + case ISSET: + _issetList.pop(); + break; + case UNSET: + _unsetList.pop(); + break; + case INVALID_FIRST: + case INVALID_LAST: + // defensive programming according to "Code Complete 2nd Edition, MS Press" + throw new IllegalStateException("IllegalState: popFieldGet with FIRST/LAST Element"); + } + } + + /* + * Returns the calling class. + */ + public QuercusClass getCallingClass() { + return _callingClass; + } + + public Value getCallingClassName() { + QuercusClass qClass = _callingClass; + + if (qClass != null) { + return createString(qClass.getName()); + } else { + warning(L.l("get_called_class() must be called from a class-context.")); + + return NullValue.NULL; + } + } + + /* + * Returns the calling class. + */ + public QuercusClass getCallingClass(Value qThis) { + QuercusClass cls = qThis.getQuercusClass(); + + if (cls == null) { + cls = _callingClass; + } + + return cls; + } + + /* + * Sets the calling class. + */ + public QuercusClass setCallingClass(QuercusClass cls) { + QuercusClass oldCallingClass = _callingClass; + + _callingClass = cls; + + return oldCallingClass; + } + + public ArrayList getStackTrace() { + ArrayList trace = new ArrayList(); + + 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().copySaveFunArg(); + } + + _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) { + return getConstant(name, true); + } + + /** + * Returns a constant. + */ + public Value getConstant(String name, boolean isAutoCreateString) { + Value value = getConstantImpl(name); + + if (value != null) { + return value; + } + + int ns = name.lastIndexOf('\\'); + + if (ns >= 0) { + name = name.substring(ns + 1); + value = getConstantImpl(name); + + if (value != null) { + return value; + } + } + + /* XXX: + notice(L.l("Converting undefined constant '{0}' to string.", + name)); + */ + + if (isAutoCreateString) { + return createString(name); + } else { + return null; + } + } + + /** + * 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; + } + } + + id = _quercus.getConstantLowerId(name); + + if (id > 0 && id < _const.length) { + Value value = _const[id]; + + if (value != null) { + return value; + } + } + + /* + id = _quercus.getConstantLower(id); + + if (id > 0 && id < _const.length) { + Value value = _const[id]; + + 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) { + _const[id] = value; + + return value; + } + + Value nameValue = _quercus.getConstantName(id); + + String name = nameValue.toString(); + + int ns = name.lastIndexOf('\\'); + if (ns >= 0) { + name = name.substring(ns + 1); + + value = getConstantImpl(name); + + if (value != null) { + _const[id] = value; + + return value; + } + } + + return nameValue; + + /* + 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; + + if (isCaseInsensitive) { + id = _quercus.addLowerConstantId(new ConstStringValue(name)); + } else { + id = _quercus.getConstantId(name); + } + + return addConstant(id, value, isCaseInsensitive); + } + + /** + * Sets a constant. + */ + public Value addConstant(StringValue name, + Value value, + boolean isCaseInsensitive) { + int id; + + if (isCaseInsensitive) { + id = _quercus.addLowerConstantId(name); + } else { + 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 (_const.length <= lowerId) { + 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 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(createString(systemFuns[i].getName())); + } else if (envFuns[i] != null + && !(envFuns[i] instanceof UndefinedFunction)) { + user.append(createString(envFuns[i].getName())); + } + } + + funs.append(createString("internal"), system); + funs.append(createString("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 int findFunctionId(String name) { + return _quercus.findFunctionId(name); + } + + /** + * 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); + } else { + return null; + } + } + + /** + * Returns the function with a given name. + * + * Compiled mode normally uses the _fun array directly, so this call + * is rare. + */ + public AbstractFunction findFunction(int id) { + if (id >= 0) { + if (id < _fun.length && !(_fun[id] instanceof UndefinedFunction)) { + return _fun[id]; + } else { + return null; + } + } + + return null; + } - 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(StringValue name) { - return getGlobalEnvVar(name, true, false); - } - - /** - * Gets a variable - * - * @param name the variable name - * @param isAutoCreate - */ - public final EnvVar getGlobalEnvVar(StringValue name, - boolean isAutoCreate, - boolean isOutputNotice) { - EnvVar envVar = _globalMap.get(name); - - if (envVar != null) { - return envVar; - } - - envVar = getSuperGlobalRef(name, true); - - 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); - } - } + public AbstractFunction getFunction(String name) { + AbstractFunction fun = findFunction(name); - if (envVar == null) { - envVar = getGlobalScriptContextRef(name); - } + if (fun != null) { + return fun; + } else { + throw createErrorException(L.l("'{0}' is an unknown function.", name)); + } + } - if (envVar == null) { - if (!isAutoCreate) { + public void updateFunction(int id, AbstractFunction fun) { + if (_fun.length <= id) { + AbstractFunction[] oldFun = _fun; - if (isOutputNotice) { - notice(L.l("${0} is an undefined variable", name)); - } + _fun = new AbstractFunction[id + 256]; + System.arraycopy(oldFun, 0, _fun, 0, oldFun.length); + } - return null; - } - - Var var = new Var(); - // var.setGlobal(); - - envVar = new EnvVarImpl(var); - } - - _globalMap.put(name, envVar); - - return envVar; - } - - /** - * Pushes a new environment. - */ - public Map pushEnv(Map map) { - Map oldEnv = _map; - - _map = map; - - return oldEnv; - } - - /** - * Restores the old environment. - */ - public void popEnv(Map oldEnv) { - _map = oldEnv; - } - - /** - * Returns the current environment. - */ - public Map getEnv() { - return _map; - } - - /** - * Returns the current environment. - */ - public Map getGlobalEnv() { - return _globalMap; - } - - public boolean isGlobalEnv() { - return _map == _globalMap; - } - - /** - * Gets a static variable name. - */ - public final StringValue createStaticName() { - return _quercus.createStaticName(); - } - - /** - * Gets a static variable - * - * @param name the variable name - */ - public final Var getStaticVar(StringValue name) { - Var var = _staticMap.get(name); - - if (var == null) { - var = new Var(); - _staticMap.put(name, var); - } - - return var; - } - - /** - * Gets a static variable - * - * @param name the variable name - */ - public final Value getStaticValue(StringValue name) { - Var var = _staticMap.get(name); - - if (var != null) { - return var.toValue(); - } else { - return NullValue.NULL; - } - } - - /** - * Gets a static variable - * - * @param name the variable name - */ - public final Var setStaticRef(StringValue name, Value value) { - if (value.isVar()) { - Var var = (Var) value; - - _staticMap.put(name, var); - - return var; - } - - Var var = _staticMap.get(name); - - if (var == null) { - var = new Var(); - _staticMap.put(name, var); - } - - var.set(value); - - return var; - } - - /** - * Gets a static variable - * - * @param name the variable name - */ - /* - public final Var getStaticClassVar(Value qThis, - String className, - String name) - { - // php/3248 - // php/324a - // php/324b - QuercusClass callingClass = getCallingClass(qThis); - - if (callingClass.isA(className)) - className = callingClass.getName(); - - StringValue varName = createString(className).append("::").append(name); - - return getStaticVar(varName); - } - */ - /** - * Unsets variable - * - * @param name the variable name - */ - public final Var unsetVar(StringValue name) { - EnvVar envVar = _map.get(name); - - if (envVar != null) { - envVar.setVar(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(StringValue name) { - EnvVar envVar = _map.get(name); - - if (envVar != null) { - envVar.setVar(new Var()); - } - - return null; - } - - /** - * Unsets variable - * - * @param name the variable name - */ - public final Var unsetGlobalVar(StringValue name) { - EnvVar envVar = _globalMap.get(name); - - if (envVar != null) { - envVar.setVar(new Var()); - } - - return null; - } - - /** - * Gets a local - * - * @param var the current value of the variable - */ - public static 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 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 Value setLocalVar(Value var, Value value) { - value = value.toValue(); - - if (var instanceof Var) { - var.set(value); - } - - return value; - } - - private EnvVar getSuperGlobalRef(StringValue name, boolean isGlobal) { - return getSuperGlobalRef(name, false, isGlobal); - } - - /** - * Returns a superglobal. - */ - private EnvVar getSuperGlobalRef(StringValue name, - boolean isCheckGlobal, - boolean isGlobal) { - 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; - } - } - } + if (_fun[id] == null) { + _fun[id] = fun; + } + } - switch (specialVarId) { - case _ENV: { - var = new Var(); - envVar = new EnvVarImpl(var); + /* + 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(this); + } + + return getFunction(name.toString()); + } - _globalMap.put(name, envVar); + /* + 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); + + // TODO: 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(); + } + + // PHP naming style for anonymous functions + String name = "\u0000lambda_" + (_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) { + // TODO: 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; + } - envVar.set(new ArrayValueImpl()); + AbstractFunction fun = cl.findFunction(methodName); - return envVar; - } + if (fun == null) { + error(L.l("'{0}::{1}' is an unknown method.", + className, methodName)); + return null; + } - case HTTP_POST_VARS: - if (!QuercusContext.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this)) { - return null; - } else { - return getGlobalEnvVar(S_POST); - } + return fun; + } - case _POST: { - var = new Var(); - envVar = new EnvVarImpl(var); + // + // 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); + } - _globalMap.put(name, envVar); + QuercusContext quercus = getQuercus(); - ArrayValue post = new ArrayValueImpl(); + QuercusProgram program = quercus.parseEvalExpr(code); - envVar.set(post); + Value value = program.execute(this); - if (_variablesOrder.indexOf('P') >= 0 - && _postArray.getSize() > 0) { - for (Map.Entry entry : _postArray.entrySet()) { - post.put(entry.getKey(), entry.getValue()); - } - } + if (value == null) { + return NullValue.NULL; + } else { + return value; + } + } - return envVar; - } + /** + * Evaluates the top-level code and prepend and append code. + */ + public void execute() + throws IOException { + StringValue prepend = _quercus.getIniValue("auto_prepend_file").toStringValue(this); - case HTTP_POST_FILES: - if (!QuercusContext.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this)) { - return null; + if (prepend.length() > 0) { + Path prependPath = lookup(prepend); + + if (prependPath == null) { + error(L.l("auto_prepend_file '{0}' not found.", prepend)); } else { - return getGlobalEnvVar(S_FILES); + QuercusPage prependPage = _quercus.parse(prependPath); + prependPage.executeTop(this); } + } - case _FILES: { - var = new Var(); - envVar = new EnvVarImpl(var); + executeTop(); - _globalMap.put(name, envVar); + StringValue append = _quercus.getIniValue("auto_append_file").toStringValue(this); - ArrayValue files = new ArrayValueImpl(); + if (append.length() > 0) { + Path appendPath = lookup(append); - if (_files != null) { - for (Map.Entry entry : _files.entrySet()) { - files.put(entry.getKey(), entry.getValue()); - } + if (appendPath == null) { + error(L.l("auto_append_file '{0}' not found.", append)); + } else { + QuercusPage appendPage = getQuercus().parse(appendPath); + appendPage.executeTop(this); } + } + } - envVar.set(files); + /** + * Evaluates the top-level code + * + * @return the result + */ + public Value executeTop() { + Path oldPwd = getPwd(); - return envVar; - } + Path pwd = _page.getPwd(this); - case HTTP_GET_VARS: - if (!QuercusContext.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this)) { - return null; - } else if (!isGlobal) { - return null; - } else { - return getGlobalEnvVar(S_GET); - } + setPwd(pwd); - case _GET: { - if (isCheckGlobal) { - EnvVar e = _globalMap.get(name); + try { + return executePageTop(_page); + } catch (QuercusLanguageException e) { + log.log(Level.FINER, e.toString(), e); - if (e != null) { - return e; - } + if (getExceptionHandler() != null) { + try { + getExceptionHandler().call(this, e.getValue()); + } catch (QuercusLanguageException e2) { + uncaughtExceptionError(e2); + } + } else { + uncaughtExceptionError(e); } - var = new Var(); - envVar = new EnvVarImpl(var); - - _globalMap.put(name, envVar); + return NullValue.NULL; + } finally { + setPwd(oldPwd); + } + } - ArrayValue array = new ArrayValueImpl(); - envVar.set(array); + /* + * Throws an error for this uncaught exception. + */ + private void uncaughtExceptionError(QuercusLanguageException e) { + Location location = e.getLocation(this); + String type = e.getValue().toString(); + String message = e.getMessage(this); + + if ("".equals(type)) { + type = e.getValue().getType(); + } + + if (message.equals("")) { + error(location, + L.l( + "Uncaught exception of type '{0}'", + type)); + } else { + error(location, + L.l("Uncaught exception of type '{0}' with message '{1}'", + type, + message)); + } + } + + /** + * Executes the given page + */ + protected Value executePage(QuercusPage page) { + if (log.isLoggable(Level.FINEST)) { + log.finest(this + " executePage " + 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, + _staticMap, + _globalMap, + _includeMap, + _importMap); + } + + EnvVar[] getGlobalList() { + return _globalList; + } + + /** + * Returns true for any special variables, i.e. which should not be + * saved + */ + boolean isSpecialVar(StringValue name) { + if (_quercus.isSuperGlobal(name)) { + return true; + } else if (_scriptGlobalMap.get(name) != null) { + return true; + } - if (_variablesOrder.indexOf('G') >= 0) { - fillGet(array, getIniBoolean("magic_quotes_gpc")); - } + return false; + } - return envVar; - } + /** + * Restores to a given state + */ + public void restoreState(SaveState saveState) { + AbstractFunction[] fun = saveState.getFunctionList(); + if (_fun.length < fun.length) { + _fun = new AbstractFunction[fun.length]; + } - case _REQUEST: { - var = new Var(); - envVar = new EnvVarImpl(var); + System.arraycopy(fun, 0, _fun, 0, fun.length); - ArrayValue array = new ArrayValueImpl(); + ClassDef[] classDef = saveState.getClassDefList(); + if (_classDef.length < classDef.length) { + _classDef = new ClassDef[classDef.length]; + } - envVar.set(array); + System.arraycopy(classDef, 0, _classDef, 0, classDef.length); - _globalMap.put(name, envVar); + QuercusClass[] qClass = saveState.getQuercusClassList(); + if (_qClass.length < qClass.length) { + _qClass = new QuercusClass[qClass.length]; + } - if (_request == null) { - return envVar; - } + System.arraycopy(qClass, 0, _qClass, 0, qClass.length); - boolean isMagicQuotes = getIniBoolean("magic_quotes_gpc"); - - int orderLen = _variablesOrder.length(); - - for (int i = 0; i < orderLen; i++) { - switch (_variablesOrder.charAt(i)) { - case 'G': - fillGet(array, isMagicQuotes); - break; - case 'P': - if (_postArray.getSize() > 0) { - fillPost(array, _postArray); - } - break; - case 'C': - fillCookies(array, _request.getCookies(), isMagicQuotes); - break; - } - } + Value[] constList = saveState.getConstantList(); + if (_const.length < constList.length) { + _const = new Value[constList.length]; + } - return envVar; - } + System.arraycopy(constList, 0, _const, 0, constList.length); - case HTTP_RAW_POST_DATA: { - if (!QuercusContext.INI_ALWAYS_POPULATE_RAW_POST_DATA.getAsBoolean(this)) { - String contentType = getContentType(); + IntMap staticNameMap = saveState.getStaticNameMap(); + Value[] staticList = saveState.getStaticList(); - if (contentType == null || !contentType.equals("unknown/type")) { - return null; - } - } + _staticMap = new LazyStaticMap(staticNameMap, staticList); - if (_inputData == null) { - return null; - } + IntMap globalNameMap = saveState.getGlobalNameMap(); + Value[] globalList = saveState.getGlobalList(); - var = new Var(); - envVar = new EnvVarImpl(var); + Map oldGlobal = _globalMap; - _globalMap.put(name, envVar); + _globalMap = new LazySymbolMap(globalNameMap, globalList); + _map = _globalMap; - var.set(_inputData); + // php/4045 - set the vars for any active EnvVar entries + for (Map.Entry oldEntry : oldGlobal.entrySet()) { + EnvVar oldEnvVar = oldEntry.getValue(); - return envVar; - } + EnvVar newEnvVar = _globalMap.get(oldEntry.getKey()); - case HTTP_SERVER_VARS: - if (!QuercusContext.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this)) { - return null; - } else { - return getGlobalEnvVar(S_SERVER); + if (newEnvVar != null) { + oldEnvVar.setVar(newEnvVar.getVar()); } + } - case _SERVER: { - var = new Var(); - envVar = new EnvVarImpl(var); - - _globalMap.put(name, envVar); + // php/404j - include_once + Map includeMap = saveState.getIncludeMap(); + _includeMap = new HashMap(includeMap); - Value serverEnv; + // php/404l + // TODO: import and namespaces - if (_variablesOrder.indexOf('S') >= 0) { - serverEnv = new ServerArrayValue(this); + ImportMap importMap = saveState.getImportMap(); - String query = getQueryString(); + if (importMap != null) { + _importMap = importMap.copy(); + } + } - if (_quercus.getIniBoolean("register_argc_argv") - && query != null) { - ArrayValue argv = new ArrayValueImpl(); + /** + * Creates a stdClass object. + */ + public ObjectValue createObject() { + try { + return (ObjectValue) _quercus.getStdClass().createObject(this); + } catch (Exception e) { + throw new QuercusModuleException(e); + } + } - int i = 0; - int j = 0; - while ((j = query.indexOf('+', i)) >= 0) { - String sub = query.substring(i, j); + /** + * Creates a stdClass object. + */ + public ObjectValue createIncompleteObject(String name) { + try { + ObjectValue obj = (ObjectValue) _quercus.getStdClass().createObject(this); - argv.put(sub); + obj.setIncompleteObjectName(name); - i = j + 1; - } + return obj; - if (i < query.length()) { - argv.put(query.substring(i)); - } + } catch (Exception e) { + throw new QuercusModuleException(e); + } + } - serverEnv.put(createString("argc"), - LongValue.create(argv.getSize())); + /* + * Creates an empty string. + */ + public StringValue getEmptyString() { + if (_isUnicodeSemantics) { + return UnicodeBuilderValue.EMPTY; + } else { + return ConstStringValue.EMPTY; + } + } - serverEnv.put(createString("argv"), argv); - } + /* + * 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) { + if (s == null || s.length() == 0) { + return (_isUnicodeSemantics + ? UnicodeBuilderValue.EMPTY + : ConstStringValue.EMPTY); + } else if (s.length() == 1) { + if (_isUnicodeSemantics) { + return UnicodeBuilderValue.create(s.charAt(0)); } else { - serverEnv = new ArrayValueImpl(); + return ConstStringValue.create(s.charAt(0)); + } + } else if (_isUnicodeSemantics) { + return new UnicodeBuilderValue(s); + } else { + StringValue stringValue = _internStringMap.get(s); + + if (stringValue == null) { + stringValue = new ConstStringValue(s); + _internStringMap.put(s, stringValue); } - var.set(serverEnv); - - return envVar; - } + return stringValue; - case _GLOBAL: { - var = new Var(); - envVar = new EnvVarImpl(var); + // return new ConstStringValue(s); + } + } - _globalMap.put(name, envVar); + /** + * Creates a string from a byte. + */ + public StringValue createString(char ch) { + if (_isUnicodeSemantics) { + return UnicodeValueImpl.create(ch); + } else { + return ConstStringValue.create(ch); + } + } - var.set(new GlobalArrayValue(this)); + /** + * 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 = createString(message); + Value[] args = {messageV}; + + Value value = cls.callNew(this, args); + + Location location = getLocation(); + + value.putField(this, "file", createString(location.getFileName())); + value.putField(this, "line", LongValue.create(location.getLineNumber())); + value.putField(this, "trace", ErrorModule.debug_backtrace(this, false)); + + return value; + } + + /** + * Creates a PHP Exception. + */ + public Value createException(Throwable e) { + QuercusClass cls = findClass("Exception"); + + StringValue message = createString(e.getMessage()); + Value[] args = {message}; + + Value value = cls.callNew(this, args); + + StackTraceElement elt = e.getStackTrace()[0]; + + value.putField(this, "file", createString(elt.getFileName())); + value.putField(this, "line", LongValue.create(elt.getLineNumber())); + value.putField(this, "trace", ErrorModule.debug_backtrace(this, false)); + + if ((e instanceof QuercusException) && e.getCause() != null) { + e = e.getCause(); + } + + value.putField(this, "__javaException", wrapJava(e)); + + 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; - return envVar; - } + try { + def = _quercus.getJavaClassDefinition(className); - case HTTP_COOKIE_VARS: - if (!QuercusContext.INI_REGISTER_LONG_ARRAYS.getAsBoolean(this)) { - return null; + if (def == null && useImport) { + useImport = false; + def = importJavaClass(className); + } + } catch (Throwable e) { + if (useImport) { + def = importJavaClass(className); } else { - return getGlobalEnvVar(S_COOKIE); + 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; + } - case _COOKIE: { - var = new Var(); - envVar = new EnvVarImpl(var); + String fullName = _importMap.getQualified(className); - _globalMap.put(name, envVar); + if (fullName != null) { + return getJavaClassDefinition(fullName, false); + } else { + ArrayList wildcardList = _importMap.getWildcardList(); - if (_variablesOrder.indexOf('C') >= 0) { - var.set(getCookies()); + 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(); + } + + String phpName = _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 obj 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 { - var.set(new ArrayValueImpl()); + return NullValue.NULL; } + } + + return wrapJava(obj); + } + + /** + * Returns a PHP value for a Java object + * + * @param isNullAsFalse what to return if obj is null, if true return + * {@link BooleanValue.FALSE} otherwise return {@link NullValue.NULL} + */ + public Value wrapJava(Object obj, JavaClassDef def, boolean isNullAsFalse) { + if (obj == null) { + if (isNullAsFalse) { + return BooleanValue.FALSE; + } else { + return NullValue.NULL; + } + } - return envVar; - } - - case _SESSION: { - return _globalMap.get("_SESSION"); - } + return wrapJava(obj, def); + } - case PHP_SELF: { - var = new Var(); - envVar = new EnvVarImpl(var); + /** + * Returns a PHP value for a Java object + */ + public Value wrapJava(Object obj) { + if (obj == null) { + return NullValue.NULL; + } - _globalMap.put(name, envVar); + if (obj instanceof Value) { + return (Value) obj; + } - var.set(getGlobalVar("_SERVER").get(PHP_SELF_STRING)); + JavaClassDef def = getJavaClassDefinition(obj.getClass()); - return envVar; - } + return def.wrap(this, obj); + } - default: - return null; - } - } - - protected String getQueryString() { - if (_request != null) { - return _request.getQueryString(); - } else { - return null; - } - } - - protected String getContentType() { - if (_request != null) { - return _request.getContentType(); - } else { - return null; - } - } - - protected ArrayValue getCookies() { - ArrayValue array = new ArrayValueImpl(); - boolean isMagicQuotes = getIniBoolean("magic_quotes_gpc"); - - Cookie[] cookies = _request.getCookies(); - if (cookies != null) { - for (int i = 0; i < cookies.length; i++) { - Cookie cookie = cookies[i]; + /** + * Returns a PHP value for a Java object + * + * @param isNullAsFalse what to return if obj is null, if true return + * {@link BooleanValue.FALSE} otherwise return {@link NullValue.NULL} + */ + public Value wrapJava(Object obj, JavaClassDef def) { + if (obj == null) { + return NullValue.NULL; + } + + if (obj instanceof Value) { + return (Value) obj; + } + + // TODO: 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()) { + // php/0ceg + + // TODO: what if types are incompatible, does it matter? + // if it doesn't matter, simplify this to one if with no else + def = getJavaClassDefinition(obj.getClass()); + } + + return 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); - String value = decodeValue(cookie.getValue()); + return cl; + } else { + String name = _quercus.getClassName(id); - StringValue valueAsValue = createString(value); + QuercusClass qcl = findClassExt(name, useAutoload, useImport); - if (isMagicQuotes) // php/0876 - { - valueAsValue = StringModule.addslashes(valueAsValue); + if (qcl != null) { + _qClass[id] = qcl; + } else { + return null; } - array.append(createString(cookie.getName()), valueAsValue); - } - } + return qcl; + } + } + + private QuercusClass findClassExt(String name, + boolean useAutoload, + boolean useImport) { + int id = _quercus.getClassId(name); + + if (useAutoload) { + StringValue nameString = createString(name); + + if (!_autoloadClasses.contains(name)) { + try { + _autoloadClasses.add(name); - return array; - } + int size = _autoloadList != null ? _autoloadList.size() : 0; - public void setArgs(String[] args) { - if (_quercus.getIniBoolean("register_argc_argv")) { - ArrayValue argv = new ArrayValueImpl(); + for (int i = 0; i < size; i++) { + Callable cb = _autoloadList.get(i); - for (String arg : args) { - argv.put(arg); - } + cb.call(this, nameString); - Value serverEnv = getGlobalValue("_SERVER"); + // php/0977 + QuercusClass cls = findClass(name, false, useImport); - serverEnv.put(createString("argc"), - LongValue.create(args.length)); + if (cls != null) { + return cls; + } + } - serverEnv.put(createString("argv"), argv); - } - } + if (size == 0) { + if (_autoload == null) { + _autoload = findFunction("__autoload"); + } - /** - * Gets a value. - */ - protected EnvVar getGlobalSpecialRef(StringValue name) { - if (QuercusContext.isSuperGlobal(name)) { - return _globalMap.get(name); - } else { - return null; - } - } + if (_autoload != null) { + _autoload.call(this, nameString); - protected EnvVar getGlobalScriptContextRef(StringValue name) { - if (_scriptContext == null) { - return null; - } + // php/0976 + QuercusClass cls = findClass(name, false, useImport); - EnvVar envVar = _globalMap.get(name); + 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); - if (envVar != null) { - return envVar; - } + _qClass[id] = cls; - Object value = _scriptContext.getAttribute(name.toString()); + cls.init(this); - if (value == null) { - Bindings bindings = _scriptContext.getBindings(ScriptContext.ENGINE_SCOPE); + return cls; + } + } catch (Exception e) { + log.log(Level.FINER, e.toString(), e); + } + } + } - if (bindings != null) { - value = bindings.get(name.toString()); - } - } + return _internalAutoload.loadClass(this, name); + } - if (value == null) { - Bindings bindings = _scriptContext.getBindings(ScriptContext.GLOBAL_SCOPE); + /** + * Returns the class with the given id + */ + public QuercusClass getClass(int classId) { + if (_qClass.length <= classId) { + QuercusClass[] oldClassList = _qClass; - if (bindings != null) { - value = bindings.get(name.toString()); - } - } + _qClass = new QuercusClass[classId + 256]; - if (value != null) { - envVar = new EnvVarImpl(new Var()); + System.arraycopy(oldClassList, 0, _qClass, 0, oldClassList.length); + } - _globalMap.put(name, envVar); + QuercusClass qClass = _qClass[classId]; - envVar.set(wrapJava(value)); - } + if (qClass != null) { + return qClass; + } - return envVar; - } + if (_classDef.length <= classId) { + ClassDef[] oldClassDefList = _classDef; - protected static String decodeValue(String s) { - int len = s.length(); - StringBuilder sb = new StringBuilder(); + _classDef = new ClassDef[classId + 256]; - for (int i = 0; i < len; i++) { - char ch = s.charAt(i); + System.arraycopy(oldClassDefList, 0, + _classDef, 0, + oldClassDefList.length); + } - if (ch == '%' && i + 2 < len) { - int d1 = s.charAt(i + 1); - int d2 = s.charAt(i + 2); + ClassDef def = _classDef[classId]; - int v = 0; + if (def == null) { + QuercusClass cl = findClass(classId, true, true); - 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); + if (cl != null) { + return cl; } else { - sb.append('%'); - continue; - } + error(L.l("'{0}' is an unknown class.", + _quercus.getClassName(classId))); - 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; + + throw new QuercusException(L.l("'{0}' is an unknown class.", + _quercus.getClassName(classId))); } + } - i += 2; - sb.append((char) v); - } else if (ch == '+') { - sb.append(' '); - } else { - sb.append(ch); - } - } - - return sb.toString(); - } - - public Var getVar(String name) { - return getVar(createString(name)); - } - - /** - * Gets a value. - */ - public Var getVar(StringValue name) { - EnvVar envVar = getEnvVar(name); - - return envVar.getVar(); - } - - /** - * Gets a value. - */ - public Var getGlobalVar(String name) { - EnvVar envVar = getGlobalEnvVar(createString(name)); - - return envVar.getVar(); - } - - /** - * Gets a value. - */ - public Var getGlobalVar(StringValue name) { - EnvVar envVar = getGlobalEnvVar(name); - - return envVar.getVar(); - } - - public void setValue(String name, Value value) { - // TODO: update to Quercus - setValue(createString(name), value); - } - - /** - * Sets a value. value must not be a Var. - */ - public Value setValue(StringValue name, Value value) { - EnvVar envVar = getEnvVar(name); - - envVar.set(value); - - return value; - } - - /** - * Sets a variable. - */ - public Var setVar(StringValue name, Var var) { - EnvVar envVar = getEnvVar(name); - - envVar.setVar(var); - - return var; - } - - /** - * Sets a value. - */ - public Var setRef(StringValue name, Value value) { - EnvVar envVar = getEnvVar(name); - - return envVar.setRef(value); - } - - /** - * Sets a value. - */ - /* - public static Var toRef(Value value) - { - // php/3243 - - if (value instanceof Var) - return (Var) value; - else - return new Var(value); - } - */ - /** - * Convert to a reference argument. It's a PHP error to convert anything - * but a var or null to a ref arg. - */ - /* - public Value toRefArgument(Value value) - { - // php/33lg - - if (value instanceof Var) - return (Var) value; - else if (value instanceof NullValue) - return NullValue.NULL; - else { - warning(L.l("'{0}' is an invalid reference, " - + "because only variables may be passed by reference.", - value)); - - return NullValue.NULL; - } - } - */ - /** - * External calls to set a global value. - */ - public Value setGlobalValue(String name, Value value) { - return setGlobalValue(createString(name), value); - } - - /** - * External calls to set a global value. - */ - public Value setGlobalValue(StringValue name, Value value) { - EnvVar envVar = getGlobalEnvVar(name); - - envVar.setRef(value); - - return value; - } - - /** - * 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 name doesn't already exist on the - * field __get() stack. - */ - public boolean pushFieldGet(Env.OVERLOADING_TYPES type, String className, StringValue fieldName) { - FieldGetEntry entry = new FieldGetEntry(className, fieldName); - - LinkedList list = null; - switch (type) { - case FIELDGET: - list = _fieldGetList; - break; - case FIELDSET: - list = _fieldSetList; - break; - case ISSET: - list = _issetList; - break; - case UNSET: - list = _unsetList; - break; - case INVALID_FIRST: - case INVALID_LAST: - // defensive programming according to "Code Complete 2nd Edition, MS Press" - throw new IllegalStateException("IllegalState: pushFieldGet with FIRST/LAST Element"); - } - - if (list == null) { - return false; - } - - if (list.contains(entry)) { - return false; - } else { - list.push(entry); - - return true; - } - } - - public void popFieldGet(Env.OVERLOADING_TYPES type) { - switch (type) { - case FIELDGET: - _fieldGetList.pop(); - break; - case FIELDSET: - _fieldSetList.pop(); - break; - case ISSET: - _issetList.pop(); - break; - case UNSET: - _unsetList.pop(); - break; - case INVALID_FIRST: - case INVALID_LAST: - // defensive programming according to "Code Complete 2nd Edition, MS Press" - throw new IllegalStateException("IllegalState: popFieldGet with FIRST/LAST Element"); - } - } - - /* - * Returns the calling class. - */ - public QuercusClass getCallingClass() { - return _callingClass; - } - - public Value getCallingClassName() { - QuercusClass qClass = _callingClass; - - if (qClass != null) { - return createString(qClass.getName()); - } else { - warning(L.l("get_called_class() must be called from a class-context.")); - - return NullValue.NULL; - } - } - - /* - * Returns the calling class. - */ - public QuercusClass getCallingClass(Value qThis) { - QuercusClass cls = qThis.getQuercusClass(); - - if (cls == null) { - cls = _callingClass; - } - - return cls; - } - - /* - * Sets the calling class. - */ - public QuercusClass setCallingClass(QuercusClass cls) { - QuercusClass oldCallingClass = _callingClass; - - _callingClass = cls; - - return oldCallingClass; - } - - public ArrayList getStackTrace() { - ArrayList trace = new ArrayList(); - - 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().copySaveFunArg(); - } - - _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) { - return getConstant(name, true); - } - - /** - * Returns a constant. - */ - public Value getConstant(String name, boolean isAutoCreateString) { - Value value = getConstantImpl(name); - - if (value != null) { - return value; - } - - int ns = name.lastIndexOf('\\'); - - if (ns >= 0) { - name = name.substring(ns + 1); - value = getConstantImpl(name); - - if (value != null) { - return value; - } - } - - /* XXX: - notice(L.l("Converting undefined constant '{0}' to string.", - name)); - */ - - if (isAutoCreateString) { - return createString(name); - } else { - return null; - } - } - - /** - * 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 parentId = -1; - id = _quercus.getConstantLowerId(name); + if (def.getParentName() != null) { + parentId = _quercus.getClassId(def.getParentName()); + } - if (id > 0 && id < _const.length) { - Value value = _const[id]; + addClass(def, classId, parentId); - if (value != null) { - return value; - } - } + return _qClass[classId]; + } - /* - id = _quercus.getConstantLower(id); + /** + * 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(); - if (id > 0 && id < _const.length) { - Value value = _const[id]; + // php/0cn2 - make sure interfaces have a QuercusClass + /* XXX: temp, needs to be argument + for (String iface : def.getInterfaces()) { + QuercusClass cl = findClass(iface); + } + */ - if (value != null) - return value; - } - */ + QuercusClass parentClass = null; - return null; + if (parentId >= 0) { + parentClass = getClass(parentId); + } - /* - value = _quercus.getConstant(name); - if (value != null) - return value; + QuercusClass qClass = _quercus.getCachedClass(classId); - if (_lowerConstMap != null) { - value = _lowerConstMap.get(name.toLowerCase()); + if (qClass == null + || qClass.isModified() + || qClass.getClassDef() != def + || qClass.getParent() != parentClass) { + qClass = createQuercusClass(classId, def, parentClass); - if (value != null) - return value; - } + _quercus.setCachedClass(classId, qClass); + } - return null; - */ - } + if (_qClass.length <= classId) { + QuercusClass[] oldClassList = _qClass; - /** - * Returns a constant. - */ - public Value getConstant(int id) { - if (_const.length <= id) { - return _quercus.getConstantName(id); - } + _qClass = new QuercusClass[classId + 256]; - Value value = _const[id]; + System.arraycopy(oldClassList, 0, _qClass, 0, oldClassList.length); + } - if (value != null) { - return value; - } + _qClass[classId] = qClass; + qClass.init(this); + } - int lowerId = _quercus.getConstantLower(id); + public void addClass(String name, ClassDef def) { + int id = _quercus.getClassId(name); - value = _const[lowerId]; - if (value != null) { - _const[id] = value; + int parentId = -1; - return value; - } + if (def.getParentName() != null) { + parentId = _quercus.getClassId(def.getParentName()); + } - Value nameValue = _quercus.getConstantName(id); + addClass(def, id, parentId); + } - String name = nameValue.toString(); + /** + * 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]; - int ns = name.lastIndexOf('\\'); - if (ns >= 0) { - name = name.substring(ns + 1); + String parentName = classDef.getParentName(); - value = getConstantImpl(name); + QuercusClass parent = null; - if (value != null) { - _const[id] = value; + if (parentName != null) { + parent = findClass(parentName); + } - return value; - } - } - - return nameValue; - - /* - 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; - - if (isCaseInsensitive) { - StringValue newname = null; - if (isUnicodeSemantics()) { - newname = new UnicodeBuilderValue(name); - } else { - newname = new ConstStringValue(name); - } - id = _quercus.addLowerConstantId(newname); - } else { - id = _quercus.getConstantId(name); - } - - return addConstant(id, value, isCaseInsensitive); - } - - /** - * Sets a constant. - */ - public Value addConstant(StringValue name, - Value value, - boolean isCaseInsensitive) { - int id; - - if (isCaseInsensitive) { - id = _quercus.addLowerConstantId(name); - } else { - 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 (_const.length <= lowerId) { - Value[] newConst = new Value[lowerId + 256]; - System.arraycopy(_const, 0, newConst, 0, _const.length); + if (parentName == null || parent != null) { + return createQuercusClass(id, classDef, parent); + } else { + return null; // php/ + } + } - _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 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(createString(systemFuns[i].getName())); - } else if (envFuns[i] != null - && !(envFuns[i] instanceof UndefinedFunction)) { - user.append(createString(envFuns[i].getName())); - } - } - - funs.append(createString("internal"), system); - funs.append(createString("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 int findFunctionId(String name) { - return _quercus.findFunctionId(name); - } - - /** - * 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); - } else { - return null; - } - } - - /** - * Returns the function with a given name. - * - * Compiled mode normally uses the _fun array directly, so this call - * is rare. - */ - public AbstractFunction findFunction(int id) { - if (id >= 0) { - if (id < _fun.length && !(_fun[id] instanceof UndefinedFunction)) { - return _fun[id]; - } else { + ClassDef staticClass = _quercus.getClassDef(id); + + if (staticClass != null) { + return createQuercusClass(id, staticClass, null); // TODO: cache + } else { return null; - } - } - - 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(this); - } - - 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); - - // TODO: 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(); - } - - // PHP naming style for anonymous functions - String name = "\u0000lambda_" + (_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) { - // TODO: 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); - } - - QuercusContext 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 and prepend and append code. - */ - public void execute() - throws IOException { - StringValue prepend = _quercus.getIniValue("auto_prepend_file").toStringValue(this); - - if (prepend.length() > 0) { - Path prependPath = lookup(prepend); - - if (prependPath == null) { - error(L.l("auto_prepend_file '{0}' not found.", prepend)); - } else { - QuercusPage prependPage = _quercus.parse(prependPath); - prependPage.executeTop(this); - } - } - - executeTop(); - - StringValue append = _quercus.getIniValue("auto_append_file").toStringValue(this); - - if (append.length() > 0) { - Path appendPath = lookup(append); - - if (appendPath == null) { - error(L.l("auto_append_file '{0}' not found.", append)); - } else { - QuercusPage appendPage = getQuercus().parse(appendPath); - appendPage.executeTop(this); - } - } - } - - /** - * 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); + * Registers an SPL autoload function. + */ + public void addAutoloadFunction(Callable fun) { + if (fun == null) { + throw new NullPointerException(); + } + + if (_autoloadList == null) { + _autoloadList = new ArrayList(); + } + + _autoloadList.add(fun); + } + + /* + * Unregisters an SPL autoload function. + */ + public void removeAutoloadFunction(Callable fun) { + if (_autoloadList != null) { + _autoloadList.remove(fun); + + //restore original __autoload functionality + if (_autoloadList.size() == 0) { + _autoloadList = null; } - } 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().toString(); - String message = e.getMessage(this); - - if ("".equals(type)) { - type = e.getValue().getType(); - } - - if (message.equals("")) { - error(location, - L.l( - "Uncaught exception of type '{0}'", - type)); - } else { - error(location, - L.l("Uncaught exception of type '{0}' with message '{1}'", - type, - message)); - } - } - - /** - * Executes the given page - */ - protected Value executePage(QuercusPage page) { - if (log.isLoggable(Level.FINEST)) { - log.finest(this + " executePage " + 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, - _staticMap, - _globalMap, - _includeMap, - _importMap); - } - - EnvVar[] getGlobalList() { - return _globalList; - } - - /** - * Returns true for any special variables, i.e. which should not be - * saved - */ - boolean isSpecialVar(StringValue 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 staticNameMap = saveState.getStaticNameMap(); - Value[] staticList = saveState.getStaticList(); - - _staticMap = new LazyStaticMap(staticNameMap, staticList); - - IntMap globalNameMap = saveState.getGlobalNameMap(); - Value[] globalList = saveState.getGlobalList(); - - Map oldGlobal = _globalMap; - - _globalMap = new LazySymbolMap(globalNameMap, globalList); - _map = _globalMap; - - // php/4045 - set the vars for any active EnvVar entries - for (Map.Entry oldEntry : oldGlobal.entrySet()) { - EnvVar oldEnvVar = oldEntry.getValue(); - - EnvVar newEnvVar = _globalMap.get(oldEntry.getKey()); - - if (newEnvVar != null) { - oldEnvVar.setVar(newEnvVar.getVar()); - } - } - - // php/404j - include_once - Map includeMap = saveState.getIncludeMap(); - _includeMap = new HashMap(includeMap); - - // php/404l - // TODO: import and namespaces - - ImportMap importMap = saveState.getImportMap(); - - if (importMap != null) { - _importMap = importMap.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) { - if (s == null || s.length() == 0) { - return (_isUnicodeSemantics - ? UnicodeBuilderValue.EMPTY - : ConstStringValue.EMPTY); - } else if (s.length() == 1) { - if (_isUnicodeSemantics) { - return UnicodeBuilderValue.create(s.charAt(0)); - } else { - return ConstStringValue.create(s.charAt(0)); - } - } else if (_isUnicodeSemantics) { - return new UnicodeBuilderValue(s); - } else { - StringValue stringValue = _internStringMap.get(s); - - if (stringValue == null) { - stringValue = new ConstStringValue(s); - _internStringMap.put(s, stringValue); - } - - return stringValue; - - // return new ConstStringValue(s); - } - } - - /** - * Creates a string from a byte. - */ - public StringValue createString(char ch) { - if (_isUnicodeSemantics) { - return UnicodeValueImpl.create(ch); - } else { - return ConstStringValue.create(ch); - } - } - - /** - * 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 = createString(message); - Value[] args = {messageV}; - - Value value = cls.callNew(this, args); - - Location location = getLocation(); - - value.putField(this, "file", createString(location.getFileName())); - value.putField(this, "line", LongValue.create(location.getLineNumber())); - value.putField(this, "trace", ErrorModule.debug_backtrace(this, false)); - - return value; - } - - /** - * Creates a PHP Exception. - */ - public Value createException(Throwable e) { - QuercusClass cls = findClass("Exception"); - - StringValue message = createString(e.getMessage()); - Value[] args = {message}; - - Value value = cls.callNew(this, args); - - StackTraceElement elt = e.getStackTrace()[0]; - - value.putField(this, "file", createString(elt.getFileName())); - value.putField(this, "line", LongValue.create(elt.getLineNumber())); - value.putField(this, "trace", ErrorModule.debug_backtrace(this, false)); - - if ((e instanceof QuercusException) && e.getCause() != null) { - e = e.getCause(); - } - - value.putField(this, "__javaException", wrapJava(e)); - - 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; - } + /* + * Returns the registered SPL autoload functions. + */ + public ArrayList 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; + } - /** - * 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.getQualifiedPhp(name); - String fullName = _importMap.getQualified(className); + URL url = null; + ClassLoader loader = Thread.currentThread().getContextClassLoader(); - if (fullName != null) { - return getJavaClassDefinition(fullName, false); - } else { - ArrayList wildcardList = _importMap.getWildcardList(); + if (fullName != null) { + url = loader.getResource(fullName); + } else { + for (String entry : _importMap.getWildcardPhpList()) { - for (String entry : wildcardList) { - fullName = entry + '.' + className; + url = loader.getResource(entry + '/' + name + ".php"); - JavaClassDef def = getJavaClassDefinition(fullName, false); + if (url != null) { + break; + } + } + } - if (def != null) { - _importMap.putQualified(className, fullName); - return def; + 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 list = new ArrayList(); + + for (int i = 0; i < _classDef.length; i++) { + if (_classDef[i] != null) { + list.add(_classDef[i].getName()); } - } - } - - 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(); - } - - String phpName = _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 obj 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 obj is null, if true return - * {@link BooleanValue.FALSE} otherwise return {@link NullValue.NULL} - */ - public Value wrapJava(Object obj, JavaClassDef 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 - */ - public Value wrapJava(Object obj) { - if (obj == null) { - return NullValue.NULL; - } - - if (obj instanceof Value) { - return (Value) obj; - } - - JavaClassDef def = getJavaClassDefinition(obj.getClass()); - - return def.wrap(this, obj); - } - - /** - * Returns a PHP value for a Java object - * - * @param isNullAsFalse what to return if obj is null, if true return - * {@link BooleanValue.FALSE} otherwise return {@link NullValue.NULL} - */ - public Value wrapJava(Object obj, JavaClassDef def) { - if (obj == null) { - return NullValue.NULL; - } - - if (obj instanceof Value) { - return (Value) obj; - } - - // TODO: 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()) { - // php/0ceg - - // TODO: what if types are incompatible, does it matter? - // if it doesn't matter, simplify this to one if with no else - def = getJavaClassDefinition(obj.getClass()); - } - - return 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; - } - } + HashMap classMap = getModuleContext().getClassMap(); - private QuercusClass findClassExt(String name, - boolean useAutoload, - boolean useImport) { - int id = _quercus.getClassId(name); + for (Map.Entry entry : classMap.entrySet()) { + list.add(entry.getKey()); + } - if (useAutoload) { - StringValue nameString = createString(name); + Collections.sort(list); - if (!_autoloadClasses.contains(name)) { - try { - _autoloadClasses.add(name); + ArrayValue array = new ArrayValueImpl(); + + Iterator iter = list.iterator(); + while (iter.hasNext()) { + array.put(iter.next()); + } - int size = _autoloadList != null ? _autoloadList.size() : 0; + return array; + } - for (int i = 0; i < size; i++) { - Callable cb = _autoloadList.get(i); + /** + * 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); - cb.call(this, nameString); + 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(createString(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("'{0}' ({1}) is an unknown callback name", + nameV, nameV.getClass().getSimpleName())); + + 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); + } + + return new CallbackObjectMethod(this, obj, createString(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 CallbackObjectMethod(this, cl, createString(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 include path", include)); + return BooleanValue.FALSE; + } else { + warning(L.l("'{0}' is not a valid include path", include)); + return BooleanValue.FALSE; + } - // php/0977 - QuercusClass cls = findClass(name, false, useImport); + // php/0b2d + if (!_isAllowUrlInclude && isUrl(path)) { + String msg = (L.l("not allowed to include url {0}", path.getURL())); - if (cls != null) { - return cls; - } - } + log.warning(dbgId() + msg); + error(msg); - if (size == 0) { - if (_autoload == null) { - _autoload = findFunction("__autoload"); - } + return BooleanValue.FALSE; + } - if (_autoload != null) { - _autoload.call(this, nameString); + QuercusPage page = _includeMap.get(path); - // php/0976 - QuercusClass cls = findClass(name, false, useImport); + if (page != null && isOnce) { + return BooleanValue.TRUE; + } else if (page == null || page.isModified()) { + page = _quercus.parse(path); - if (cls != null) { - return cls; - } - } - } - } finally { - _autoloadClasses.remove(name); + pageInit(page); + + _includeMap.put(path, page); } - } - } - if (useImport) { - if (importPhpClass(name)) { - return findClass(name, false, false); - } else { - try { - JavaClassDef javaClassDef = getJavaClassDefinition(name, true); + return executePage(page); + } catch (IOException e) { + throw new QuercusModuleException(e); + } + } - if (javaClassDef != null) { - QuercusClass cls = createQuercusClass(id, javaClassDef, null); + void executePage(Path path) { + if (log.isLoggable(Level.FINEST)) { + log.finest(this + " execute " + path); + } - _qClass[id] = cls; + try { + QuercusPage page = _quercus.parse(path); - cls.init(this); + pageInit(page); - return cls; - } - } catch (Exception e) { - log.log(Level.FINER, e.toString(), e); - } - } - } + 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; + } + + if (path instanceof JarPath) { + // php/0h1a + return isUrl(((JarPath) path).getContainer()); + } + + // TODO: 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; + } - return _internalAutoload.loadClass(this, name); - } + StringValue relPath = relPathV.toStringValue(); - /** - * Returns the class with the given id - */ - public QuercusClass getClass(int classId) { - if (_qClass.length <= classId) { - QuercusClass[] oldClassList = _qClass; + if (relPath.length() == 0) { + return null; + } - _qClass = new QuercusClass[classId + 256]; + Path path = _lookupCache.get(relPath); - System.arraycopy(oldClassList, 0, _qClass, 0, oldClassList.length); - } + if (path == null) { + path = getPwd().lookup(normalizePath(relPath)); + _lookupCache.put(relPath, path); + } - QuercusClass qClass = _qClass[classId]; + return path; + } - if (qClass != null) { - return qClass; - } + /** + * Looks up the path. + */ + public Path lookup(StringValue relPath) { + return lookupInclude(getSelfDirectory(), normalizePath(relPath)); + } - if (_classDef.length <= classId) { - ClassDef[] oldClassDefList = _classDef; + /** + * Looks up the path. + */ + public Path lookupInclude(StringValue relPath) { + return lookupInclude(relPath, getPwd(), getSelfDirectory()); + } - _classDef = new ClassDef[classId + 256]; + private Path lookupInclude(StringValue include, Path pwd, Path scriptPwd) { + String includePath = getDefaultIncludePath(); - System.arraycopy(oldClassDefList, 0, - _classDef, 0, - oldClassDefList.length); - } + Path path = _quercus.getIncludeCache(include, includePath, pwd, scriptPwd); - ClassDef def = _classDef[classId]; + if (path == null) { + path = lookupIncludeImpl(include, pwd, scriptPwd); - if (def == null) { - QuercusClass cl = findClass(classId, true, true); + /* + if (path == null) + path = NullPath.NULL; + */ - if (cl != null) { - return cl; - } else { - error(L.l("'{0}' is an unknown class.", - _quercus.getClassName(classId))); + if (path != null) { + _quercus.putIncludeCache(include, includePath, pwd, scriptPwd, path); + } + } + if (path == NullPath.NULL) { + path = null; + } - throw new QuercusException(L.l("'{0}' is an unknown class.", - _quercus.getClassName(classId))); - } - } + _includePath = includePath; + _includePathIniCount = _iniCount; - int parentId = -1; + return path; + } - if (def.getParentName() != null) { - parentId = _quercus.getClassId(def.getParentName()); - } + private String getDefaultIncludePath() { + String includePath = _includePath; - addClass(def, classId, parentId); + if (_includePathIniCount != _iniCount) { + includePath = QuercusContext.INI_INCLUDE_PATH.getAsString(this); + _includePath = null; + _includePathList = null; + } - return _qClass[classId]; - } + if (includePath == null) { + includePath = "."; + } - /** - * 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(); + return includePath; + } - // 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); // TODO: cache - } else { - return null; - } - } - - /* - * Registers an SPL autoload function. - */ - public void addAutoloadFunction(Callable fun) { - if (fun == null) { - throw new NullPointerException(); - } - - if (_autoloadList == null) { - _autoloadList = new ArrayList(); - } - - _autoloadList.add(fun); - } - - /* - * Unregisters an SPL autoload function. - */ - public void removeAutoloadFunction(Callable 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 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; + 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; } - } - } - - 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 list = new ArrayList(); - - for (int i = 0; i < _classDef.length; i++) { - if (_classDef[i] != null) { - list.add(_classDef[i].getName()); - } - } - - HashMap classMap = getModuleContext().getClassMap(); - - for (Map.Entry entry : classMap.entrySet()) { - list.add(entry.getKey()); - } - - Collections.sort(list); - - ArrayValue array = new ArrayValueImpl(); - - Iterator 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(createString(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("'{0}' ({1}) is an unknown callback name", - nameV, nameV.getClass().getSimpleName())); - - 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); - } - - return new CallbackObjectMethod(this, obj, createString(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 CallbackObjectMethod(this, cl, createString(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 include path", include)); - return BooleanValue.FALSE; - } else { - warning(L.l("'{0}' is not a valid include 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); + return path; + } + + /** + * Looks up the path. + */ + private Path lookupInclude(Path pwd, String relPath) { + ArrayList pathList = getIncludePath(pwd); + + for (int i = 0; i < pathList.size(); i++) { + Path path = pathList.get(i).lookup(relPath); - _includeMap.put(path, page); - } - - return executePage(page); - } catch (IOException e) { - throw new QuercusModuleException(e); - } - } - - void executePage(Path path) { - if (log.isLoggable(Level.FINEST)) { - log.finest(this + " execute " + 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; - } - - if (path instanceof JarPath) { - // php/0h1a - return isUrl(((JarPath) path).getContainer()); - } - - // TODO: 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(); - - 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 = QuercusContext.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); + if (path.canRead() && !path.isDirectory()) { + return path; + } + } - // php/0b0g + return null; + } - Path path = lookupInclude(pwd, include); + private boolean includeExists(Path path) { + if (path.canRead() && !path.isDirectory()) { + return true; + } else if (!getQuercus().isRequireSource()) { + return getQuercus().includeExists(path); + } else { + return false; + } + } - if (path == null) { - // php/0b0l - path = lookupInclude(scriptPwd, include); - } + /** + * Returns the include path. + */ + private ArrayList getIncludePath(Path pwd) { + String includePath = getDefaultIncludePath(); - if (path == null) { - // php/0b21 - path = scriptPwd.lookup(include); + if (_includePathList == null) { + _includePathList = new ArrayList(); + _includePathMap = new HashMap>(); - if (!includeExists(path)) { - path = null; - } - } - - return path; - } - - /** - * Looks up the path. - */ - private Path lookupInclude(Path pwd, String relPath) { - ArrayList 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 getIncludePath(Path pwd) { - String includePath = getDefaultIncludePath(); - - if (_includePathList == null) { - _includePathList = new ArrayList(); - _includePathMap = new HashMap>(); - - 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); + int head = 0; + int tail; - _includePathList.add(normalizePath(subpath)); + String pathSeparator = FileModule.PATH_SEPARATOR; + int length = pathSeparator.length(); + + while ((tail = includePath.indexOf(pathSeparator, head)) >= 0) { + String subpath = includePath.substring(head, tail); - head = tail + length; - } + _includePathList.add(normalizePath(subpath)); + + head = tail + length; + } - String subpath = includePath.substring(head); + String subpath = includePath.substring(head); - _includePathList.add(normalizePath(subpath)); + _includePathList.add(normalizePath(subpath)); - _includePath = includePath; - _includePathIniCount = _iniCount; - } + _includePath = includePath; + _includePathIniCount = _iniCount; + } - ArrayList pathList = _includePathMap.get(pwd); + ArrayList pathList = _includePathMap.get(pwd); - if (pathList == null) { - pathList = new ArrayList(); + if (pathList == null) { + pathList = new ArrayList(); - for (int i = 0; i < _includePathList.size(); i++) { - pathList.add(pwd.lookup(_includePathList.get(i))); - } + for (int i = 0; i < _includePathList.size(); i++) { + pathList.add(pwd.lookup(_includePathList.get(i))); + } - _includePathMap.put(pwd, pathList); - } + _includePathMap.put(pwd, pathList); + } - return pathList; - } + return pathList; + } - /** - * Sets the include path. - */ - public String setIncludePath(String path) { - String prevIncludePath = QuercusContext.INI_INCLUDE_PATH.getAsString(this); + /** + * Sets the include path. + */ + public String setIncludePath(String path) { + String prevIncludePath = QuercusContext.INI_INCLUDE_PATH.getAsString(this); - if (_defaultIncludePath == null) { - _defaultIncludePath = prevIncludePath; - } + if (_defaultIncludePath == null) { + _defaultIncludePath = prevIncludePath; + } - QuercusContext.INI_INCLUDE_PATH.set(this, path); + QuercusContext.INI_INCLUDE_PATH.set(this, path); - // reset include path cache count - _includePathIniCount = -1; + // reset include path cache count + _includePathIniCount = -1; - return prevIncludePath; - } + return prevIncludePath; + } - public String normalizePath(CharSequence path) { - if (Path.isWindows()) { - _cb.setLength(0); + public String normalizePath(CharSequence path) { + if (Path.isWindows()) { + _cb.setLength(0); - int len = path.length(); + int len = path.length(); - if (len >= 3) { - char ch; - char ch3; + 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); + 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)); - } + for (int i = 3; i < len; i++) { + _cb.append(path.charAt(i)); + } - return _cb.toString(); + 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() { + QuercusContext.INI_INCLUDE_PATH.set(this, _defaultIncludePath); + } + + /** + * Returns all the included files. + */ + public ArrayValue getIncludedFiles() { + ArrayValue array = new ArrayValueImpl(); + + ArrayList list = new ArrayList(); + + for (Path path : _includeMap.keySet()) { + list.add(path.getNativePath()); + } + + Collections.sort(list); + + for (String pathName : list) { + array.put(createString(pathName)); + } + + 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); + } - return path.toString(); + 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 expected type. + */ + public Value expectString(Value value) { + if (!value.isString() + && !value.isLong() + && !value.isDouble() + && !value.isBoolean() + && !value.isNull()) { + //warning(L.l("argument must be a string, but {0} given", + // value.getType())); + + return new UnexpectedValue(value); + } + + return value; + } + + /** + * Check for expected type. + */ + public Value expectNumeric(Value value) { + if (!value.isNull() + && !value.isLongConvertible()) { + //warning(L.l("argument must be numeric, but {0} given", + // value.getType())); + + return new UnexpectedValue(value); + } + + return value; + } + + /** + * Check for expected type. + */ + public Value expectBoolean(Value value) { + if (!value.isBoolean() + && !value.isNull() + && !value.isString() + && !value.isNumeric()) { + //warning(L.l("argument must be boolean, but {0} given", + // value.getType())); + return new UnexpectedValue(value); + } + + 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.isUnknown()) { + location = getLocation(); + } + + String prefix = location.getMessagePrefix(); + + String fullMsg = msg + getFunctionLocation(); + + error(B_ERROR, location, fullMsg); + + String exMsg = prefix + fullMsg; + + return new QuercusRuntimeException(fullMsg); + } + + /** + * A runtime warning. + */ + public Value warning(String msg) { + int mask = 1 << B_WARNING; + + if ((getErrorMask() & 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 (len >= 3) { - char ch = path.charAt(0); - char ch2 = path.charAt(1); - char ch3 = path.charAt(2); + if ((getErrorMask() & mask) != 0) { + if (log.isLoggable(Level.FINER)) { + QuercusException e = new QuercusException(msg); - _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() { - QuercusContext.INI_INCLUDE_PATH.set(this, _defaultIncludePath); - } - - /** - * Returns all the included files. - */ - public ArrayValue getIncludedFiles() { - ArrayValue array = new ArrayValueImpl(); - - ArrayList list = new ArrayList(); - - for (Path path : _includeMap.keySet()) { - list.add(path.getNativePath()); - } - - Collections.sort(list); - - for (String pathName : list) { - array.put(createString(pathName)); - } - - 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 expected type. - */ - public Value expectString(Value value) { - if (!value.isString() - && !value.isLong() - && !value.isDouble() - && !value.isBoolean() - && !value.isNull()) { - //warning(L.l("argument must be a string, but {0} given", - // value.getType())); - - return new UnexpectedValue(value); - } - - return value; - } - - /** - * Check for expected type. - */ - public Value expectNumeric(Value value) { - if (!value.isNull() - && !value.isLongConvertible()) { - //warning(L.l("argument must be numeric, but {0} given", - // value.getType())); - - return new UnexpectedValue(value); - } - - return value; - } - - /** - * Check for expected type. - */ - public Value expectBoolean(Value value) { - if (!value.isBoolean() - && !value.isNull() - && !value.isString() - && !value.isNumeric()) { - //warning(L.l("argument must be boolean, but {0} given", - // value.getType())); - return new UnexpectedValue(value); - } - - 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.isUnknown()) { - location = getLocation(); - } - - String prefix = location.getMessagePrefix(); - - String fullMsg = msg + getFunctionLocation(); - - error(B_ERROR, location, fullMsg); - - String exMsg = prefix + fullMsg; - - return new QuercusRuntimeException(fullMsg); - } - - /** - * A runtime warning. - */ - public Value warning(String msg) { - int mask = 1 << B_WARNING; - - if ((getErrorMask() & mask) != 0) { - if (log.isLoggable(Level.FINER)) { + 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 getIni("error_reporting").toInt(); + } + + /** + * Sets the error mask. + */ + public int setErrorMask(int mask) { + int oldMask = getErrorMask(); + + setIni("error_reporting", LongValue.create(mask)); + + return oldMask; + } + + /** + * Sets an error handler + */ + public void setErrorHandler(int mask, Callable 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 Callable getExceptionHandler() { + return _exceptionHandler; + } + + /** + * Sets an exception handler + */ + public Value setExceptionHandler(Callable fun) { + _prevExceptionHandler = _exceptionHandler; + + _exceptionHandler = fun; + + if (_prevExceptionHandler != null) { + // + return ((Value) _prevExceptionHandler).toStringValue(); + } else { + return NullValue.NULL; + } + } - return error(B_WARNING, "", msg + getFunctionLocation()); - } + /** + * Restore an exception handler + */ + public void restoreExceptionHandler() { + _exceptionHandler = _prevExceptionHandler; + } - /** - * A runtime warning. - */ - public Value warning(Location location, String msg) { - int mask = 1 << B_WARNING; + /* + * Writes an error. + */ + public Value error(int code, String locString, String msg) { + return error(code, null, locString, msg); + } - if ((getErrorMask() & mask) != 0) { - if (log.isLoggable(Level.FINER)) { - QuercusException e = new QuercusException(msg); + /* + * Writes an error. + */ + public Value error(int code, Location location, String msg) { + return error(code, location, "", 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 getIni("error_reporting").toInt(); - } - - /** - * Sets the error mask. - */ - public int setErrorMask(int mask) { - int oldMask = getErrorMask(); - - setIni("error_reporting", LongValue.create(mask)); - - return oldMask; - } - - /** - * Sets an error handler - */ - public void setErrorHandler(int mask, Callable 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 Callable getExceptionHandler() { - return _exceptionHandler; - } - - /** - * Sets an exception handler - */ - public Value setExceptionHandler(Callable fun) { - _prevExceptionHandler = _exceptionHandler; - - _exceptionHandler = fun; - - if (_prevExceptionHandler != null) { - // - return ((Value) _prevExceptionHandler).toStringValue(); - } 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; - - int errorMask = getErrorMask(); - - 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) { - Callable handler = _errorHandlers[code]; - - try { - _errorHandlers[code] = null; - - Value fileNameV = NullValue.NULL; - - if (location == null || location.isUnknown()) { - location = getLocation(); - } + /** + * Writes an error. + */ + public Value error(int code, Location location, String loc, String msg) { + int mask = 1 << code; - String fileName = location.getFileName(); + int errorMask = getErrorMask(); - if (fileName != null) { - fileNameV = createString(fileName); - } + if (log.isLoggable(Level.FINEST)) { + QuercusException e = new QuercusException(loc + msg); + + log.log(Level.FINEST, e.toString(), e); + } - Value lineV = NullValue.NULL; - int line = location.getLineNumber(); - if (line > 0) { - lineV = LongValue.create(line); + if ((errorMask & mask) != 0) { + if (log.isLoggable(Level.FINE)) { + log.fine(this + " " + loc + msg); } + } - Value context = NullValue.NULL; + if (code >= 0 && code < _errorHandlers.length + && _errorHandlers[code] != null) { + Callable handler = _errorHandlers[code]; - handler.call(this, LongValue.create(mask), createString(msg), - fileNameV, lineV, context); + try { + _errorHandlers[code] = null; - return NullValue.NULL; - } catch (RuntimeException e) { - throw e; - } catch (Exception 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", createString(fullMsg)); + Value fileNameV = NullValue.NULL; + + if (location == null || location.isUnknown()) { + location = getLocation(); + } + + String fileName = location.getFileName(); + + if (fileName != null) { + fileNameV = createString(fileName); + } + + 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), createString(msg), + fileNameV, lineV, context); + + return NullValue.NULL; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + _errorHandlers[code] = handler; } + } - if ("stderr".equals(getIniString("display_errors"))) { - // initial newline to match PHP - System.err.println("\n" + fullMsg); - } else if (getIniBoolean("display_errors")) { - // initial newline to match PHP - getOut().println("\n" + fullMsg); + if ((errorMask & mask) != 0) { + try { + String fullMsg = (getLocationPrefix(location, loc) + + getCodeName(mask) + msg); + + if (getIniBoolean("track_errors")) { + setGlobalValue("php_errormsg", createString(fullMsg)); + } + + if ("stderr".equals(getIniString("display_errors"))) { + // initial newline to match PHP + System.err.println("\n" + fullMsg); + } else if (getIniBoolean("display_errors")) { + // initial newline to match PHP + getOut().println("\n" + 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); + QuercusErrorException exn; + + if (!"".equals(locPrefix)) { + /* + throw new QuercusLineExitException(getLocation() + + getCodeName(mask) + + msg); + */ + exn = new QuercusErrorException(locPrefix + + getCodeName(mask) + + msg); + } else { + exn = new QuercusErrorException(msg); } - if (getIniBoolean("log_errors")) { - log.info(fullMsg); + exn.fillInStackTrace(); + + if (log.isLoggable(Level.FINER)) { + log.log(Level.FINER, exn.toString(), exn); } - } 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); - QuercusErrorException exn; + throw exn; + } + + 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.isUnknown()) { + 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 + // TODO: not too pretty + return null; + } + + ReadStream is = null; - if (!"".equals(locPrefix)) { - /* - throw new QuercusLineExitException(getLocation() + - getCodeName(mask) + - msg); - */ - exn = new QuercusErrorException(locPrefix - + getCodeName(mask) - + msg); - } else { - exn = new QuercusErrorException(msg); - } - - exn.fillInStackTrace(); - - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, exn.toString(), exn); - } - - throw exn; - } - - 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.isUnknown()) { - 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 - // TODO: not too pretty - return null; - } - - ReadStream is = null; - - try { - is = path.openRead(); - - int line = 1; - String lineString; - - for (; line < sourceLine; line++) { - lineString = is.readLine(); - - if (lineString == null) { - return null; + try { + is = path.openRead(); + + int line = 1; + String lineString; + + for (; line < sourceLine; line++) { + lineString = is.readLine(); + + if (lineString == null) { + return null; + } } - } - - String[] result = new String[length]; - - int i = line - sourceLine; - for (; i < length && (lineString = is.readLine()) != null; i++) { - result[i] = lineString; - } - - return result; - } catch (IOException e) { - log.log(Level.FINE, e.toString(), e); - } finally { - if (is != null) { - is.close(); - } - } - - return null; - } - - public final Location setLocation(Location newLocation) { - Location location = _location; - _location = newLocation; - - return location; - } - - protected final Location getLocationImpl() { - return _location; - } - - /** - * Returns the current execution location. - * - * Use with care, for compiled code this can be a relatively expensive - * operation. - */ - public Location getLocation() { - Location location = _location; - - if (location != null) { - return location; - } - - 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() { - // TODO: 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 - // TODO: 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; - } - - /** - * Calls a parent::name method. - */ - /* - public Value callParentMethod(Value qThis, - String parentName, - String funName, - Value []args) - { - AbstractFunction fun - = getClass(parentName).getFunction(funName); - - Value retValue = fun.callMethod(this, qThis, args); - - if (fun.isConstructor()) - qThis.setJavaObject(retValue); - - return retValue; - } - */ - /** - * Calls a parent::name method. - */ - /* - public Value callParentMethod(Value qThis, - String parentName, - int hash, - char []name, - int len, - Value []args) - { - - AbstractFunction fun - = getClass(parentName).getFunction(hash, name, len); - - Value retValue = fun.callMethod(this, qThis, args); - - if (fun.isConstructor()) - qThis.setJavaObject(retValue); - - return retValue; - } - */ - 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() { - long nanoTime = _quercus.getExactTimeNanoseconds(); - - if (_firstMicroTime <= 0) { - _firstNanoTime = nanoTime; - - _firstMicroTime = _quercus.getExactTime() * 1000 - + (_firstNanoTime % 1000000L) / 1000; - - return _firstMicroTime; - } else { - long microDiff = (nanoTime - _firstNanoTime) / 1000; - - return _firstMicroTime + microDiff; - } - } - - /** - * Registers a shutdown function. - */ - public void addShutdown(Callable callback, Value[] args) { - if (_shutdownList == null) { - _shutdownList = new ArrayList(); - } - - _shutdownList.add(new Shutdown(callback, args)); - } - - // TODO: hack until can clean up - public void setGzStream(Object obj) { - _gzStream = obj; - } - - // TODO: hack until can clean up - public Object getGzStream() { - return _gzStream; - } - - public void startDuplex(Object duplex) { - if (_duplex != null) { - return; - } - - _duplex = duplex; - } - - public void closeDuplex() { - _duplex = null; - - close(); - } - - public Object getDuplex() { - return _duplex; - } - - /** - * Called when the Env is no longer needed. - */ - public void close() { - _quercus.completeEnv(this); - - if (_duplex != null) { - log.fine(this + " skipping close for duplex mode"); - return; - } - - 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); + + String[] result = new String[length]; + + int i = line - sourceLine; + for (; i < length && (lineString = is.readLine()) != null; i++) { + result[i] = lineString; } - } - } - 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); + return result; + } catch (IOException e) { + log.log(Level.FINE, e.toString(), e); + } finally { + if (is != null) { + is.close(); + } + } + + return null; + } + + public final Location setLocation(Location newLocation) { + Location location = _location; + _location = newLocation; + + return location; + } + + protected final Location getLocationImpl() { + return _location; + } + + /** + * Returns the current execution location. + * + * Use with care, for compiled code this can be a relatively expensive + * operation. + */ + public Location getLocation() { + Location location = _location; + + if (location != null) { + return location; + } + + 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() { + // TODO: 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 + // TODO: 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; + } + + /** + * Calls a parent::name method. + */ + /* + public Value callParentMethod(Value qThis, + String parentName, + String funName, + Value []args) + { + AbstractFunction fun + = getClass(parentName).getFunction(funName); + + Value retValue = fun.callMethod(this, qThis, args); + + if (fun.isConstructor()) + qThis.setJavaObject(retValue); + + return retValue; + } + */ + /** + * Calls a parent::name method. + */ + /* + public Value callParentMethod(Value qThis, + String parentName, + int hash, + char []name, + int len, + Value []args) + { + + AbstractFunction fun + = getClass(parentName).getFunction(hash, name, len); + + Value retValue = fun.callMethod(this, qThis, args); + + if (fun.isConstructor()) + qThis.setJavaObject(retValue); + + return retValue; + } + */ + 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() { + long nanoTime = _quercus.getExactTimeNanoseconds(); + + if (_firstMicroTime <= 0) { + _firstNanoTime = nanoTime; + + _firstMicroTime = _quercus.getExactTime() * 1000 + + (_firstNanoTime % 1000000L) / 1000; + + return _firstMicroTime; + } else { + long microDiff = (nanoTime - _firstNanoTime) / 1000; + + return _firstMicroTime + microDiff; + } + } + + /** + * Registers a shutdown function. + */ + public void addShutdown(Callable callback, Value[] args) { + if (_shutdownList == null) { + _shutdownList = new ArrayList(); + } + + _shutdownList.add(new Shutdown(callback, args)); + } + + // TODO: hack until can clean up + public void setGzStream(Object obj) { + _gzStream = obj; + } + + // TODO: hack until can clean up + public Object getGzStream() { + return _gzStream; + } + + public void startDuplex(Object duplex) { + if (_duplex != null) { + return; + } + + _duplex = duplex; + } + + public void closeDuplex() { + _duplex = null; + + close(); + } + + public Object getDuplex() { + return _duplex; + } + + /** + * Called when the Env is no longer needed. + */ + public void close() { + _quercus.completeEnv(this); + + if (_duplex != null) { + log.fine(this + " skipping close for duplex mode"); + return; + } + + 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 cleanupList = new ArrayList(_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); + } } - } - } + } - try { - sessionWriteClose(); - } catch (Throwable e) { - log.log(Level.FINE, e.toString(), e); - } + _threadEnv.set(_oldThreadEnv); - if (_cleanupList != null) { - ArrayList cleanupList = new ArrayList(_cleanupList); + for (int i = 0; _removePaths != null && i < _removePaths.size(); i++) { + Path path = _removePaths.get(i); - // 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); + path.remove(); + } catch (IOException e) { + log.log(Level.FINER, e.toString(), e); + } + } + + AbstractFunction[] fun = _fun; + _fun = null; + if (fun != null) { + boolean isUsed = false; + + /** + * Fix for Bug #4077 + * Page may be null when if the Env was created throught + * the (QuercusContext)-Constructor and therefor every + * parameter except the context is null. + */ + if (_page != null && _page.setRuntimeFunction(fun)) { + isUsed = true; } - } - } - _threadEnv.set(_oldThreadEnv); + for (QuercusPage page : _includeMap.values()) { + if (page.setRuntimeFunction(fun)) { + isUsed = true; + } + } - for (int i = 0; _removePaths != null && i < _removePaths.size(); i++) { - Path path = _removePaths.get(i); + if (!isUsed) { + for (int i = fun.length - 1; i >= 0; i--) { + fun[i] = null; + } - try { - path.remove(); - } catch (IOException e) { - log.log(Level.FINER, e.toString(), e); - } - } - - AbstractFunction[] fun = _fun; - _fun = null; - if (fun != null) { - boolean isUsed = false; - - /** - * Fix for Bug #4077 - * Page may be null when if the Env was created throught - * the (QuercusContext)-Constructor and therefor every - * parameter except the context is null. - */ - if (_page != null && _page.setRuntimeFunction(fun)) { - isUsed = true; - } - - for (QuercusPage page : _includeMap.values()) { - if (page.setRuntimeFunction(fun)) { - isUsed = true; + _freeFunList.free(fun); } - } - - if (!isUsed) { - for (int i = fun.length - 1; i >= 0; i--) { - fun[i] = null; + } + + ClassDef[] classDef = _classDef; + _classDef = null; + if (classDef != null) { + // php/0b3b + for (int i = 0; i < classDef.length; i++) { + classDef[i] = null; } - _freeFunList.free(fun); - } - } + _freeClassDefList.free(classDef); + } - ClassDef[] classDef = _classDef; - _classDef = null; - if (classDef != null) { - // php/0b3b - for (int i = 0; i < classDef.length; i++) { - classDef[i] = null; - } + QuercusClass[] qClass = _qClass; + _qClass = null; + if (qClass != null) { + for (int i = 0; i < qClass.length; i++) { + qClass[i] = null; + } - _freeClassDefList.free(classDef); - } + _freeClassList.free(qClass); + } - QuercusClass[] qClass = _qClass; - _qClass = null; - if (qClass != null) { - for (int i = 0; i < qClass.length; i++) { - qClass[i] = null; - } + Value[] consts = _const; + _const = null; + if (consts != null) { + for (int i = 0; i < consts.length; i++) { + consts[i] = null; + } - _freeClassList.free(qClass); - } + _freeConstList.free(consts); + } - Value[] consts = _const; - _const = null; - if (consts != null) { - for (int i = 0; i < consts.length; i++) { - consts[i] = null; - } + if (_gmtDate != null) { + _freeGmtDateList.free(_gmtDate); + } - _freeConstList.free(consts); - } + if (_localDate != null) { + _freeLocalDateList.free(_localDate); + } + } - if (_gmtDate != null) { - _freeGmtDateList.free(_gmtDate); - } + public void sessionWriteClose() { + SessionArrayValue session = _session; - if (_localDate != null) { - _freeLocalDateList.free(_localDate); - } - } + _session = null; - public void sessionWriteClose() { - SessionArrayValue session = _session; + if (session != null) { + SessionCallback callback = getSessionCallback(); - _session = null; + if (callback != null) { + String value; - if (session != null) { - SessionCallback callback = getSessionCallback(); + // php/1k6e + if (session.getSize() > 0) { + value = VariableModule.serialize(this, session.getArray()); + } else { + value = ""; + } - if (callback != null) { - String value; + callback.write(this, session.getId(), value); - // php/1k6e - if (session.getSize() > 0) { - value = VariableModule.serialize(this, session.getArray()); + callback.close(this); } else { - value = ""; - } + _quercus.saveSession(this, session); - callback.write(this, session.getId(), value); + Value sessionCopy = session.copy(this); - callback.close(this); - } else { - _quercus.saveSession(this, session); - - Value sessionCopy = session.copy(this); + setGlobalValue("_SESSION", sessionCopy); + setGlobalValue("HTTP_SESSION_VARS", sessionCopy); + } + } + } - setGlobalValue("_SESSION", sessionCopy); - setGlobalValue("HTTP_SESSION_VARS", sessionCopy); - } - } - } + public String dbgId() { + return getClass().getSimpleName() + "[" + _selfPath + "] "; + } - public String dbgId() { - return getClass().getSimpleName() + "[" + _selfPath + "] "; - } + static class FieldGetEntry { - static class FieldGetEntry { + private final String _className; + private final StringValue _fieldName; - private final String _className; - private final StringValue _fieldName; + FieldGetEntry(String className, StringValue fieldName) { + _className = className; + _fieldName = fieldName; + } - FieldGetEntry(String className, StringValue fieldName) { - _className = className; - _fieldName = fieldName; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof FieldGetEntry)) { + return false; + } - @Override - public boolean equals(Object o) { - if (!(o instanceof FieldGetEntry)) { - return false; - } + FieldGetEntry entry = (FieldGetEntry) o; - FieldGetEntry entry = (FieldGetEntry) o; + return entry._className.equals(_className) + && entry._fieldName.equals(_fieldName); + } + } - return entry._className.equals(_className) - && entry._fieldName.equals(_fieldName); - } - } + static class ClassKey { - static class ClassKey { + private final WeakReference _defRef; + private final WeakReference _parentRef; + private final int _hash; - private final WeakReference _defRef; - private final WeakReference _parentRef; - private final int _hash; + ClassKey(ClassDef def, QuercusClass parent) { + _defRef = new WeakReference(def); - ClassKey(ClassDef def, QuercusClass parent) { - _defRef = new WeakReference(def); + if (parent != null) { + _parentRef = new WeakReference(parent); + } else { + _parentRef = null; + } - if (parent != null) { - _parentRef = new WeakReference(parent); - } else { - _parentRef = null; - } + // hash needs to be precalculated so losing a weak references won't + // change the result - // hash needs to be precalculated so losing a weak references won't - // change the result + int hash = 37; - int hash = 37; + if (def != null) { + hash = 65521 * hash + def.hashCode(); + } - if (def != null) { - hash = 65521 * hash + def.hashCode(); - } + if (parent != null) { + hash = 65521 * hash + parent.hashCode(); + } - if (parent != null) { - hash = 65521 * hash + parent.hashCode(); - } + _hash = hash; + } - _hash = hash; - } + @Override + public int hashCode() { + return _hash; + } - @Override - public int hashCode() { - return _hash; - } + @Override + public boolean equals(Object o) { + ClassKey key = (ClassKey) o; - @Override - public boolean equals(Object o) { - ClassKey key = (ClassKey) o; + ClassDef aDef = _defRef.get(); + ClassDef bDef = key._defRef.get(); - ClassDef aDef = _defRef.get(); - ClassDef bDef = key._defRef.get(); + if (aDef != bDef) { + return false; + } - if (aDef != bDef) { - return false; - } + if (_parentRef == key._parentRef) { + return true; + } else if (_parentRef == null || key._parentRef == null) { + 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(MethodIntern.intern("GLOBALS"), _GLOBAL); - SPECIAL_VARS.put(MethodIntern.intern("_SERVER"), _SERVER); - SPECIAL_VARS.put(MethodIntern.intern("_GET"), _GET); - SPECIAL_VARS.put(MethodIntern.intern("_POST"), _POST); - SPECIAL_VARS.put(MethodIntern.intern("_FILES"), _FILES); - SPECIAL_VARS.put(MethodIntern.intern("_REQUEST"), _REQUEST); - SPECIAL_VARS.put(MethodIntern.intern("_COOKIE"), _COOKIE); - SPECIAL_VARS.put(MethodIntern.intern("_SESSION"), _SESSION); - SPECIAL_VARS.put(MethodIntern.intern("_ENV"), _ENV); - SPECIAL_VARS.put(MethodIntern.intern("HTTP_GET_VARS"), HTTP_GET_VARS); - SPECIAL_VARS.put(MethodIntern.intern("HTTP_POST_VARS"), HTTP_POST_VARS); - SPECIAL_VARS.put(MethodIntern.intern("HTTP_POST_FILES"), HTTP_POST_FILES); - SPECIAL_VARS.put(MethodIntern.intern("HTTP_COOKIE_VARS"), HTTP_COOKIE_VARS); - SPECIAL_VARS.put(MethodIntern.intern("HTTP_SERVER_VARS"), HTTP_SERVER_VARS); - SPECIAL_VARS.put(MethodIntern.intern("PHP_SELF"), PHP_SELF); - SPECIAL_VARS.put(MethodIntern.intern("HTTP_RAW_POST_DATA"), - HTTP_RAW_POST_DATA); - - DEFAULT_QUERY_SEPARATOR_MAP = new int[128]; - DEFAULT_QUERY_SEPARATOR_MAP['&'] = 1; - } + 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(MethodIntern.intern("GLOBALS"), _GLOBAL); + SPECIAL_VARS.put(MethodIntern.intern("_SERVER"), _SERVER); + SPECIAL_VARS.put(MethodIntern.intern("_GET"), _GET); + SPECIAL_VARS.put(MethodIntern.intern("_POST"), _POST); + SPECIAL_VARS.put(MethodIntern.intern("_FILES"), _FILES); + SPECIAL_VARS.put(MethodIntern.intern("_REQUEST"), _REQUEST); + SPECIAL_VARS.put(MethodIntern.intern("_COOKIE"), _COOKIE); + SPECIAL_VARS.put(MethodIntern.intern("_SESSION"), _SESSION); + SPECIAL_VARS.put(MethodIntern.intern("_ENV"), _ENV); + SPECIAL_VARS.put(MethodIntern.intern("HTTP_GET_VARS"), HTTP_GET_VARS); + SPECIAL_VARS.put(MethodIntern.intern("HTTP_POST_VARS"), HTTP_POST_VARS); + SPECIAL_VARS.put(MethodIntern.intern("HTTP_POST_FILES"), HTTP_POST_FILES); + SPECIAL_VARS.put(MethodIntern.intern("HTTP_COOKIE_VARS"), HTTP_COOKIE_VARS); + SPECIAL_VARS.put(MethodIntern.intern("HTTP_SERVER_VARS"), HTTP_SERVER_VARS); + SPECIAL_VARS.put(MethodIntern.intern("PHP_SELF"), PHP_SELF); + SPECIAL_VARS.put(MethodIntern.intern("HTTP_RAW_POST_DATA"), + HTTP_RAW_POST_DATA); + + DEFAULT_QUERY_SEPARATOR_MAP = new int[128]; + DEFAULT_QUERY_SEPARATOR_MAP['&'] = 1; + } } diff --git a/quercus/src/main/java/com/caucho/quercus/env/QuercusClass.java b/quercus/src/main/java/com/caucho/quercus/env/QuercusClass.java index 9e33db3..e7faf08 100644 --- a/quercus/src/main/java/com/caucho/quercus/env/QuercusClass.java +++ b/quercus/src/main/java/com/caucho/quercus/env/QuercusClass.java @@ -52,1769 +52,1771 @@ */ public class QuercusClass extends NullValue { - private static final L10N L = new L10N(QuercusClass.class); - private static final Logger log = Logger.getLogger(QuercusClass.class.getName()); - private final JavaClassDef _javaClassDef; - private final ClassDef _classDef; - private final String _className; - private QuercusClass _parent; - private WeakReference _cacheRef; - private boolean _isAbstract; - private boolean _isInterface; - private boolean _isJavaWrapper; - private ClassDef[] _classDefList; - private AbstractFunction _constructor; - private AbstractFunction _destructor; - private AbstractFunction _fieldGet; - private AbstractFunction _fieldSet; - private AbstractFunction _call; - private AbstractFunction _invoke; - private AbstractFunction _toString; - private AbstractFunction _isset; - private AbstractFunction _unset; - private ArrayDelegate _arrayDelegate; - private TraversableDelegate _traversableDelegate; - private CountDelegate _countDelegate; - private final ArrayList _initializers; - private final MethodMap _methodMap; - private final HashMap _constMap; - private final HashMap _constJavaMap; - private final LinkedHashMap _fieldMap; - private final HashMap> _staticFieldExprMap; - private final HashMap _staticFieldNameMap; - private final HashSet _instanceofSet; - private boolean _isModified; - - public QuercusClass(ClassDef classDef, QuercusClass parent) { - this(ModuleContext.getLocalContext(Thread.currentThread().getContextClassLoader()), - classDef, - parent); - } - - public QuercusClass(ModuleContext moduleContext, - ClassDef classDef, - QuercusClass parent) { - _classDef = classDef.loadClassDef(); // force load of any lazy classes - _className = classDef.getName(); - _parent = parent; - - _isAbstract = _classDef.isAbstract(); - _isInterface = _classDef.isInterface(); - - _initializers = new ArrayList(); - - _fieldMap = new LinkedHashMap(); - _methodMap = new MethodMap(this, null); - _constMap = new HashMap(); - _constJavaMap = new HashMap(); - - _staticFieldExprMap = new LinkedHashMap>(); - - _staticFieldNameMap = new LinkedHashMap(); - - if (parent != null) { - _staticFieldNameMap.putAll(parent._staticFieldNameMap); - } - - JavaClassDef javaClassDef = null; - - if (classDef instanceof JavaClassDef) { - javaClassDef = (JavaClassDef) classDef; - _isJavaWrapper = !javaClassDef.isDelegate(); - } - - for (QuercusClass cls = parent; cls != null; cls = cls.getParent()) { - AbstractFunction cons = cls.getConstructor(); - - if (cons != null) { - addMethod(new StringBuilderValue(cls.getName()), cons); - } - } - - ClassDef[] classDefList; - - if (_parent != null) { - classDefList = new ClassDef[parent._classDefList.length + 1]; - - System.arraycopy(parent._classDefList, 0, classDefList, 1, - parent._classDefList.length); - - classDefList[0] = classDef; - } else { - classDefList = new ClassDef[]{classDef}; - } - - _classDefList = classDefList; - - for (int i = 0; i < classDefList.length; i++) { - if (classDefList[i] instanceof JavaClassDef) { - javaClassDef = (JavaClassDef) classDefList[i]; - } - } - - _javaClassDef = javaClassDef; - - _instanceofSet = new HashSet(); - - HashSet ifaces = new HashSet(); - - // add interfaces - for (int i = classDefList.length - 1; i >= 0; i--) { - classDef = classDefList[i]; - - if (classDef == null) { - throw new NullPointerException("classDef:" + _classDef - + " i:" + i + " parent:" + parent); - } - - classDef.init(); + private static final L10N L = new L10N(QuercusClass.class); + private static final Logger log = Logger.getLogger(QuercusClass.class.getName()); + private final JavaClassDef _javaClassDef; + private final ClassDef _classDef; + private final String _className; + private QuercusClass _parent; + private WeakReference _cacheRef; + private boolean _isAbstract; + private boolean _isInterface; + private boolean _isJavaWrapper; + private ClassDef[] _classDefList; + private AbstractFunction _constructor; + private AbstractFunction _destructor; + private AbstractFunction _fieldGet; + private AbstractFunction _fieldSet; + private AbstractFunction _call; + private AbstractFunction _invoke; + private AbstractFunction _toString; + private AbstractFunction _isset; + private AbstractFunction _unset; + private ArrayDelegate _arrayDelegate; + private TraversableDelegate _traversableDelegate; + private CountDelegate _countDelegate; + private final ArrayList _initializers; + private final MethodMap _methodMap; + private final HashMap _constMap; + private final HashMap _constJavaMap; + private final LinkedHashMap _fieldMap; + private final HashMap> _staticFieldExprMap; + private final HashMap _staticFieldNameMap; + private final HashSet _instanceofSet; + private boolean _isModified; + + public QuercusClass(ClassDef classDef, QuercusClass parent) { + this(ModuleContext.getLocalContext(Thread.currentThread().getContextClassLoader()), + classDef, + parent); + } + + public QuercusClass(ModuleContext moduleContext, + ClassDef classDef, + QuercusClass parent) { + _classDef = classDef.loadClassDef(); // force load of any lazy classes + _className = classDef.getName(); + _parent = parent; + + _isAbstract = _classDef.isAbstract(); + _isInterface = _classDef.isInterface(); + + _initializers = new ArrayList(); + + _fieldMap = new LinkedHashMap(); + _methodMap = new MethodMap(this, null); + _constMap = new HashMap(); + _constJavaMap = new HashMap(); + + _staticFieldExprMap = new LinkedHashMap>(); + + _staticFieldNameMap = new LinkedHashMap(); + + if (parent != null) { + _staticFieldNameMap.putAll(parent._staticFieldNameMap); + } + + JavaClassDef javaClassDef = null; + + if (classDef instanceof JavaClassDef) { + javaClassDef = (JavaClassDef) classDef; + _isJavaWrapper = !javaClassDef.isDelegate(); + } + + for (QuercusClass cls = parent; cls != null; cls = cls.getParent()) { + AbstractFunction cons = cls.getConstructor(); + + if (cons != null) { + addMethod(new StringBuilderValue(cls.getName()), cons); + } + } + + ClassDef[] classDefList; + + if (_parent != null) { + classDefList = new ClassDef[parent._classDefList.length + 1]; + + System.arraycopy(parent._classDefList, 0, classDefList, 1, + parent._classDefList.length); + + classDefList[0] = classDef; + } else { + classDefList = new ClassDef[]{classDef}; + } - addInstances(_instanceofSet, ifaces, classDef); - } + _classDefList = classDefList; - // then add concrete ancestors - for (int i = classDefList.length - 1; i >= 0; i--) { - classDef = classDefList[i]; + for (int i = 0; i < classDefList.length; i++) { + if (classDefList[i] instanceof JavaClassDef) { + javaClassDef = (JavaClassDef) classDefList[i]; + } + } + + _javaClassDef = javaClassDef; + + _instanceofSet = new HashSet(); - classDef.initClass(this); - } + HashSet ifaces = new HashSet(); - if (_constructor == null && parent != null) { - _constructor = parent.getConstructor(); - } + // add interfaces + for (int i = classDefList.length - 1; i >= 0; i--) { + classDef = classDefList[i]; + + if (classDef == null) { + throw new NullPointerException("classDef:" + _classDef + + " i:" + i + " parent:" + parent); + } - // php/093n - if (_constructor != null - && !_constructor.getName().equals("__construct")) { - addMethodIfNotExist(new StringBuilderValue("__construct"), _constructor); - addMethodIfNotExist(new StringBuilderValue(_className), _constructor); - } + classDef.init(); - if (_destructor == null && parent != null) { - _destructor = parent.getDestructor(); - } - } + addInstances(_instanceofSet, ifaces, classDef); + } - private void addInstances(HashSet instanceofSet, - HashSet ifaces, - ClassDef classDef) { - // _instanceofSet.add(classDef.getName()); - classDef.addInterfaces(instanceofSet); + // then add concrete ancestors + for (int i = classDefList.length - 1; i >= 0; i--) { + classDef = classDefList[i]; - for (String iface : classDef.getInterfaces()) { - boolean isJavaClassDef = classDef instanceof JavaClassDef; + classDef.initClass(this); + } - QuercusClass cl; + if (_constructor == null && parent != null) { + _constructor = parent.getConstructor(); + } - // TODO: php/0cn2, but this is wrong: - cl = Env.getInstance().findClass(iface, - !isJavaClassDef, - true); + // php/093n + if (_constructor != null + && !_constructor.getName().equals("__construct")) { + addMethodIfNotExist(new StringBuilderValue("__construct"), _constructor); + addMethodIfNotExist(new StringBuilderValue(_className), _constructor); + } - if (cl == null) { - throw new QuercusRuntimeException(L.l("cannot find interface {0}", - iface)); - } + if (_destructor == null && parent != null) { + _destructor = parent.getDestructor(); + } + } - // _instanceofSet.addAll(cl.getInstanceofSet()); + private void addInstances(HashSet instanceofSet, + HashSet ifaces, + ClassDef classDef) { + // _instanceofSet.add(classDef.getName()); + classDef.addInterfaces(instanceofSet); - ClassDef ifaceDef = cl.getClassDef(); - // ClassDef ifaceDef = moduleContext.findClass(iface); + for (String iface : classDef.getInterfaces()) { + boolean isJavaClassDef = classDef instanceof JavaClassDef; - if (ifaceDef != null) { - if (ifaces.add(iface)) { - addInstances(instanceofSet, ifaces, ifaceDef); + QuercusClass cl; - ifaceDef.initClass(this); + // TODO: php/0cn2, but this is wrong: + cl = Env.getInstance().findClass(iface, + !isJavaClassDef, + true); + + if (cl == null) { + throw new QuercusRuntimeException(L.l("cannot find interface {0}", + iface)); } - } - } - } - - /** - * Copy based on a cached value - */ - public QuercusClass(QuercusClass cacheClass, QuercusClass parent) { - _cacheRef = new WeakReference(cacheClass); - - _javaClassDef = cacheClass._javaClassDef; - _classDef = cacheClass._classDef; - _className = cacheClass._className; - - _isJavaWrapper = cacheClass._isJavaWrapper; - _classDefList = cacheClass._classDefList; - - _parent = parent; - - _constructor = cacheClass._constructor; - _destructor = cacheClass._destructor; - - _fieldGet = cacheClass._fieldGet; - _fieldSet = cacheClass._fieldSet; - - _call = cacheClass._call; - _invoke = cacheClass._invoke; - _toString = cacheClass._toString; - - _arrayDelegate = cacheClass._arrayDelegate; - _traversableDelegate = cacheClass._traversableDelegate; - _countDelegate = cacheClass._countDelegate; - - _initializers = cacheClass._initializers; - - _fieldMap = cacheClass._fieldMap; - _methodMap = cacheClass._methodMap; - _constMap = cacheClass._constMap; - _constJavaMap = cacheClass._constJavaMap; - - _staticFieldExprMap = cacheClass._staticFieldExprMap; - _staticFieldNameMap = cacheClass._staticFieldNameMap; - _instanceofSet = cacheClass._instanceofSet; - } - - public ClassDef getClassDef() { - return _classDef; - } - - public JavaClassDef getJavaClassDef() { - return _javaClassDef; - } - - public MethodMap getMethodMap() { - return _methodMap; - } - - public HashSet getInstanceofSet() { - return _instanceofSet; - } - - /** - * Returns the name. - */ - public String getName() { - return _className; - } - - /** - * Returns the parent class. - */ - public QuercusClass getParent() { - return _parent; - } - - /* - * Returns the class definitions for this class. - */ - public ClassDef[] getClassDefList() { - return _classDefList; - } - - /* - * Returns the name of the extension that this class is part of. - */ - public String getExtension() { - return _classDef.getExtension(); - } - - public boolean isInterface() { - return _isInterface; - } - - public boolean isAbstract() { - return _isAbstract; - } - - public boolean isFinal() { - return _classDef.isFinal(); - } - - /** - * Sets the constructor. - */ - public void setConstructor(AbstractFunction fun) { - _constructor = fun; - } - - /** - * Gets the constructor. - */ - public AbstractFunction getConstructor() { - return _constructor; - } - - /** - * Sets the destructor. - */ - public void setDestructor(AbstractFunction fun) { - _destructor = fun; - } - - /** - * Gets the destructor. - */ - public AbstractFunction getDestructor() { - return _destructor; - } - - /** - * Returns true if the class is modified for caching. - */ - public boolean isModified() { - if (_isModified) { - return true; - } else if (_parent != null) { - return _parent.isModified(); - } else { - return false; - } - } - - /** - * Mark the class as modified for caching. - */ - public void setModified() { - if (!_isModified) { - _isModified = true; - - if (_cacheRef != null) { - QuercusClass cacheClass = _cacheRef.get(); - - if (cacheClass != null) { - cacheClass.setModified(); + + // _instanceofSet.addAll(cl.getInstanceofSet()); + + ClassDef ifaceDef = cl.getClassDef(); + // ClassDef ifaceDef = moduleContext.findClass(iface); + + if (ifaceDef != null) { + if (ifaces.add(iface)) { + addInstances(instanceofSet, ifaces, ifaceDef); + + ifaceDef.initClass(this); + } } - } - } - } - - /** - * Sets the array delegate (see ArrayAccess) - */ - public void setArrayDelegate(ArrayDelegate delegate) { - if (log.isLoggable(Level.FINEST)) { - log.log(Level.FINEST, L.l("{0} adding array delegate {1}", - this, delegate)); - } - - _arrayDelegate = delegate; - } - - /** - * Gets the array delegate (see ArrayAccess) - */ - public final ArrayDelegate getArrayDelegate() { - return _arrayDelegate; - } - - /** - * Sets the traversable delegate - */ - public void setTraversableDelegate(TraversableDelegate delegate) { - if (log.isLoggable(Level.FINEST)) { - log.log(Level.FINEST, L.l("{0} setting traversable delegate {1}", - this, delegate)); - } - - _traversableDelegate = delegate; - } - - /** - * Gets the traversable delegate - */ - public final TraversableDelegate getTraversableDelegate() { - return _traversableDelegate; - } - - /** - * Sets the count delegate - */ - public void setCountDelegate(CountDelegate delegate) { - if (log.isLoggable(Level.FINEST)) { - log.log(Level.FINEST, L.l("{0} setting count delegate {1}", - this, delegate)); - } - - _countDelegate = delegate; - } - - /** - * Gets the count delegate - */ - public final CountDelegate getCountDelegate() { - return _countDelegate; - } - - /** - * Sets the __fieldGet - */ - public void setFieldGet(AbstractFunction fun) { - _fieldGet = fun; - } - - /** - * Returns the __fieldGet - */ - public AbstractFunction getFieldGet() { - return _fieldGet; - } - - /** - * Sets the __fieldSet - */ - public void setFieldSet(AbstractFunction fun) { - _fieldSet = fun; - } - - /** - * Returns the __fieldSet - */ - public AbstractFunction getFieldSet() { - return _fieldSet; - } - - /** - * Sets the __call - */ - public void setCall(AbstractFunction fun) { - _call = fun; - } - - /** - * Gets the __call - */ - public AbstractFunction getCall() { - return _call; - } - - /** - * Sets the __invoke - */ - public void setInvoke(AbstractFunction fun) { - _invoke = fun; - } - - /** - * Gets the __invoke - */ - public AbstractFunction getInvoke() { - return _invoke; - } - - /** - * Sets the __toString - */ - public void setToString(AbstractFunction fun) { - _toString = fun; - } - - /** - * Gets the __toString - */ - public AbstractFunction getToString() { - return _toString; - } - - /** - * Adds an initializer - */ - public void addInitializer(InstanceInitializer init) { - _initializers.add(init); - } - - /** - * Adds a field. - */ - public void addField(StringValue name, - Expr initExpr, - FieldVisibility visibility) { - ClassField field = new ClassField(name, initExpr, visibility); - - _fieldMap.put(name, field); - } - - /** - * Returns a set of the fields and their initial values - */ - public HashMap getClassFields() { - return _fieldMap; - } - - /** - * Returns a set of the fields and their initial values - */ - public ClassField getClassField(StringValue name) { - return _fieldMap.get(name); - } - - /** - * Returns a set of the fields and their initial values - */ - public int findFieldIndex(StringValue name) { - throw new UnsupportedOperationException(); - } - - /** - * Returns the declared functions. - */ - public Iterable getClassMethods() { - return _methodMap.values(); - } - - /** - * Adds a method. - */ - public void addMethod(String name, AbstractFunction fun) { - addMethod(new StringBuilderValue(name), fun); - } - - /** - * Adds a method. - */ - public void addMethod(StringValue name, AbstractFunction fun) { - if (fun == null) { - throw new NullPointerException(L.l("'{0}' is a null function", name)); - } - - //php/09j9 - // TODO: this is a hack to get Zend Framework running, the better fix is - // to initialize all interface classes before any concrete classes - AbstractFunction existingFun = _methodMap.getRaw(name); - - if (existingFun == null || !fun.isAbstract()) { - _methodMap.put(name.toString(), fun); - } else if (!existingFun.isAbstract() && fun.isAbstract()) { - Env.getInstance().error(L.l("cannot make non-abstract function {0}:{1}() abstract", - getName(), name)); - } - } - - /* - * Adds a method if it does not exist. - */ - public void addMethodIfNotExist(StringValue name, AbstractFunction fun) { - if (fun == null) { - throw new NullPointerException(L.l("'{0}' is a null function", name)); - } - - //php/09j9 - // TODO: this is a hack to get Zend Framework running, the better fix is - // to initialize all interface classes before any concrete classes - AbstractFunction existingFun = _methodMap.getRaw(name); - - if (existingFun == null && !fun.isAbstract()) { - _methodMap.put(name.toString(), fun); - } - } - - /** - * Adds a static class field. - */ - public void addStaticFieldExpr(String className, String name, Expr value) { - ArrayList fieldList = _staticFieldExprMap.get(className); - - if (fieldList == null) { - fieldList = new ArrayList(); - - _staticFieldExprMap.put(className, fieldList); - } - - fieldList.add(new StaticField(name, value)); - _staticFieldNameMap.put(new ConstStringValue(name), - new ConstStringValue(className + "::" + name)); - } - - /** - * Returns the static field names. - */ - public ArrayList getStaticFieldNames() { - ArrayList names = new ArrayList(); - - if (_staticFieldExprMap != null) { - for (StringValue fieldName : _staticFieldNameMap.keySet()) { - names.add(fieldName); - } - } - - return names; - } - - /** - * Adds a constant definition - */ - public void addConstant(String name, Expr expr) { - _constMap.put(name, expr); - } - - /** - * Adds a constant definition - */ - public void addJavaConstant(String name, Object obj) { - _constJavaMap.put(name, obj); - } - - /** - * Returns the number of fields. - */ - public int getFieldSize() { - return _fieldMap.size(); - } - - public void validate(Env env) { - if (!_isAbstract && !_isInterface) { - for (AbstractFunction fun : _methodMap.values()) { - /* XXX: abstract methods need to be validated - php/393g, php/393i, php/39j2 - if (! (absFun instanceof Function)) - continue; - - Function fun = (Function) absFun; - */ - - boolean isAbstract; - - // php/093g constructor - if (_constructor != null - && fun.getName().equals(_constructor.getName())) { - isAbstract = _constructor.isAbstract(); - } else { - isAbstract = fun.isAbstract(); + } + } + + /** + * Copy based on a cached value + */ + public QuercusClass(QuercusClass cacheClass, QuercusClass parent) { + _cacheRef = new WeakReference(cacheClass); + + _javaClassDef = cacheClass._javaClassDef; + _classDef = cacheClass._classDef; + _className = cacheClass._className; + + _isJavaWrapper = cacheClass._isJavaWrapper; + _classDefList = cacheClass._classDefList; + + _parent = parent; + + _constructor = cacheClass._constructor; + _destructor = cacheClass._destructor; + + _fieldGet = cacheClass._fieldGet; + _fieldSet = cacheClass._fieldSet; + + _call = cacheClass._call; + _invoke = cacheClass._invoke; + _toString = cacheClass._toString; + + _arrayDelegate = cacheClass._arrayDelegate; + _traversableDelegate = cacheClass._traversableDelegate; + _countDelegate = cacheClass._countDelegate; + + _initializers = cacheClass._initializers; + + _fieldMap = cacheClass._fieldMap; + _methodMap = cacheClass._methodMap; + _constMap = cacheClass._constMap; + _constJavaMap = cacheClass._constJavaMap; + + _staticFieldExprMap = cacheClass._staticFieldExprMap; + _staticFieldNameMap = cacheClass._staticFieldNameMap; + _instanceofSet = cacheClass._instanceofSet; + } + + public ClassDef getClassDef() { + return _classDef; + } + + public JavaClassDef getJavaClassDef() { + return _javaClassDef; + } + + public MethodMap getMethodMap() { + return _methodMap; + } + + public HashSet getInstanceofSet() { + return _instanceofSet; + } + + /** + * Returns the name. + */ + public String getName() { + return _className; + } + + /** + * Returns the parent class. + */ + public QuercusClass getParent() { + return _parent; + } + + /* + * Returns the class definitions for this class. + */ + public ClassDef[] getClassDefList() { + return _classDefList; + } + + /* + * Returns the name of the extension that this class is part of. + */ + public String getExtension() { + return _classDef.getExtension(); + } + + public boolean isInterface() { + return _isInterface; + } + + public boolean isAbstract() { + return _isAbstract; + } + + public boolean isFinal() { + return _classDef.isFinal(); + } + + /** + * Sets the constructor. + */ + public void setConstructor(AbstractFunction fun) { + _constructor = fun; + } + + /** + * Gets the constructor. + */ + public AbstractFunction getConstructor() { + return _constructor; + } + + /** + * Sets the destructor. + */ + public void setDestructor(AbstractFunction fun) { + _destructor = fun; + } + + /** + * Gets the destructor. + */ + public AbstractFunction getDestructor() { + return _destructor; + } + + /** + * Returns true if the class is modified for caching. + */ + public boolean isModified() { + if (_isModified) { + return true; + } else if (_parent != null) { + return _parent.isModified(); + } else { + return false; + } + } + + /** + * Mark the class as modified for caching. + */ + public void setModified() { + if (!_isModified) { + _isModified = true; + + if (_cacheRef != null) { + QuercusClass cacheClass = _cacheRef.get(); + + if (cacheClass != null) { + cacheClass.setModified(); + } + } + } + } + + /** + * Sets the array delegate (see ArrayAccess) + */ + public void setArrayDelegate(ArrayDelegate delegate) { + if (log.isLoggable(Level.FINEST)) { + log.log(Level.FINEST, L.l("{0} adding array delegate {1}", + this, delegate)); + } + + _arrayDelegate = delegate; + } + + /** + * Gets the array delegate (see ArrayAccess) + */ + public final ArrayDelegate getArrayDelegate() { + return _arrayDelegate; + } + + /** + * Sets the traversable delegate + */ + public void setTraversableDelegate(TraversableDelegate delegate) { + if (log.isLoggable(Level.FINEST)) { + log.log(Level.FINEST, L.l("{0} setting traversable delegate {1}", + this, delegate)); + } + + _traversableDelegate = delegate; + } + + /** + * Gets the traversable delegate + */ + public final TraversableDelegate getTraversableDelegate() { + return _traversableDelegate; + } + + /** + * Sets the count delegate + */ + public void setCountDelegate(CountDelegate delegate) { + if (log.isLoggable(Level.FINEST)) { + log.log(Level.FINEST, L.l("{0} setting count delegate {1}", + this, delegate)); + } + + _countDelegate = delegate; + } + + /** + * Gets the count delegate + */ + public final CountDelegate getCountDelegate() { + return _countDelegate; + } + + /** + * Sets the __fieldGet + */ + public void setFieldGet(AbstractFunction fun) { + _fieldGet = fun; + } + + /** + * Returns the __fieldGet + */ + public AbstractFunction getFieldGet() { + return _fieldGet; + } + + /** + * Sets the __fieldSet + */ + public void setFieldSet(AbstractFunction fun) { + _fieldSet = fun; + } + + /** + * Returns the __fieldSet + */ + public AbstractFunction getFieldSet() { + return _fieldSet; + } + + /** + * Sets the __call + */ + public void setCall(AbstractFunction fun) { + _call = fun; + } + + /** + * Gets the __call + */ + public AbstractFunction getCall() { + return _call; + } + + /** + * Sets the __invoke + */ + public void setInvoke(AbstractFunction fun) { + _invoke = fun; + } + + /** + * Gets the __invoke + */ + public AbstractFunction getInvoke() { + return _invoke; + } + + /** + * Sets the __toString + */ + public void setToString(AbstractFunction fun) { + _toString = fun; + } + + /** + * Gets the __toString + */ + public AbstractFunction getToString() { + return _toString; + } + + /** + * Adds an initializer + */ + public void addInitializer(InstanceInitializer init) { + _initializers.add(init); + } + + /** + * Adds a field. + */ + public void addField(StringValue name, + Expr initExpr, + FieldVisibility visibility) { + ClassField field = new ClassField(name, initExpr, visibility); + + _fieldMap.put(name, field); + } + + /** + * Returns a set of the fields and their initial values + */ + public HashMap getClassFields() { + return _fieldMap; + } + + /** + * Returns a set of the fields and their initial values + */ + public ClassField getClassField(StringValue name) { + return _fieldMap.get(name); + } + + /** + * Returns a set of the fields and their initial values + */ + public int findFieldIndex(StringValue name) { + throw new UnsupportedOperationException(); + } + + /** + * Returns the declared functions. + */ + public Iterable getClassMethods() { + return _methodMap.values(); + } + + /** + * Adds a method. + */ + public void addMethod(String name, AbstractFunction fun) { + addMethod(new StringBuilderValue(name), fun); + } + + /** + * Adds a method. + */ + public void addMethod(StringValue name, AbstractFunction fun) { + if (fun == null) { + throw new NullPointerException(L.l("'{0}' is a null function", name)); + } + + //php/09j9 + // TODO: this is a hack to get Zend Framework running, the better fix is + // to initialize all interface classes before any concrete classes + AbstractFunction existingFun = _methodMap.getRaw(name); + + if (existingFun == null || !fun.isAbstract()) { + _methodMap.put(name.toString(), fun); + } else if (!existingFun.isAbstract() && fun.isAbstract()) { + Env.getInstance().error(L.l("cannot make non-abstract function {0}:{1}() abstract", + getName(), name)); + } + } + + /* + * Adds a method if it does not exist. + */ + public void addMethodIfNotExist(StringValue name, AbstractFunction fun) { + if (fun == null) { + throw new NullPointerException(L.l("'{0}' is a null function", name)); + } + + //php/09j9 + // TODO: this is a hack to get Zend Framework running, the better fix is + // to initialize all interface classes before any concrete classes + AbstractFunction existingFun = _methodMap.getRaw(name); + + if (existingFun == null && !fun.isAbstract()) { + _methodMap.put(name.toString(), fun); + } + } + + /** + * Adds a static class field. + */ + public void addStaticFieldExpr(String className, String name, Expr value) { + ArrayList fieldList = _staticFieldExprMap.get(className); + + if (fieldList == null) { + fieldList = new ArrayList(); + + _staticFieldExprMap.put(className, fieldList); + } + + fieldList.add(new StaticField(name, value)); + _staticFieldNameMap.put(new ConstStringValue(name), + new ConstStringValue(className + "::" + name)); + } + + /** + * Returns the static field names. + */ + public ArrayList getStaticFieldNames() { + ArrayList names = new ArrayList(); + + if (_staticFieldExprMap != null) { + for (StringValue fieldName : _staticFieldNameMap.keySet()) { + names.add(fieldName); + } + } + + return names; + } + + /** + * Adds a constant definition + */ + public void addConstant(String name, Expr expr) { + _constMap.put(name, expr); + } + + /** + * Adds a constant definition + */ + public void addJavaConstant(String name, Object obj) { + _constJavaMap.put(name, obj); + } + + /** + * Returns the number of fields. + */ + public int getFieldSize() { + return _fieldMap.size(); + } + + public void validate(Env env) { + if (!_isAbstract && !_isInterface) { + for (AbstractFunction fun : _methodMap.values()) { + /* XXX: abstract methods need to be validated + php/393g, php/393i, php/39j2 + if (! (absFun instanceof Function)) + continue; + + Function fun = (Function) absFun; + */ + + boolean isAbstract; + + // php/093g constructor + if (_constructor != null + && fun.getName().equals(_constructor.getName())) { + isAbstract = _constructor.isAbstract(); + } else { + isAbstract = fun.isAbstract(); + } + + if (isAbstract) { + throw env.createErrorException( + _classDef.getLocation(), + L.l( + "Abstract function '{0}' must be " + + "implemented in concrete class {1}.", + fun.getName(), + getName())); + } } + } + } - if (isAbstract) { - throw env.createErrorException( - _classDef.getLocation(), - L.l( - "Abstract function '{0}' must be " - + "implemented in concrete class {1}.", - fun.getName(), - getName())); + public void init(Env env) { + if (_staticFieldExprMap.size() == 0) { + return; + } + + for (Map.Entry> map : _staticFieldExprMap.entrySet()) { + if (env.isInitializedClass(map.getKey())) { + continue; } - } - } - } - - public void init(Env env) { - if (_staticFieldExprMap.size() == 0) { - return; - } - - for (Map.Entry> map : _staticFieldExprMap.entrySet()) { - if (env.isInitializedClass(map.getKey())) { - continue; - } - - for (StaticField field : map.getValue()) { - Value val; - Expr expr = field._expr; - - //php/096f - if (expr instanceof ClassConstExpr) { - val = ((ClassConstExpr) expr).eval(env); - } else { - val = expr.eval(env); + + for (StaticField field : map.getValue()) { + Value val; + Expr expr = field._expr; + + //php/096f + if (expr instanceof ClassConstExpr) { + val = ((ClassConstExpr) expr).eval(env); + } else { + val = expr.eval(env); + } + + StringValue fullName = env.createStringBuilder(); + fullName.append(_className); + fullName.append("::"); + fullName.append(field._name); + + env.setStaticRef(fullName, val); } - StringValue fullName = env.createStringBuilder(); - fullName.append(_className); - fullName.append("::"); - fullName.append(field._name); - - env.setStaticRef(fullName, val); - } - - env.addInitializedClass(map.getKey()); - } - } - - public Value getStaticFieldValue(Env env, StringValue name) { - StringValue staticName = _staticFieldNameMap.get(name); - - if (staticName == null) { - env.error(L.l("{0}::${1} is an undeclared static field", - _className, name)); - - return NullValue.NULL; - } - - return env.getStaticValue(staticName); - } - - public Var getStaticFieldVar(Env env, StringValue name) { - StringValue staticName = _staticFieldNameMap.get(name); - - if (staticName == null) { - env.error(L.l("{0}::${1} is an undeclared static field", - _className, name)); - - throw new IllegalStateException(); - } - - return env.getStaticVar(staticName); - } - - public Value setStaticFieldRef(Env env, StringValue name, Value value) { - StringValue staticName = _staticFieldNameMap.get(name); - - if (staticName == null) { - env.error(L.l("{0}::{1} is an unknown static field", - _className, name)); - - throw new IllegalStateException(); - } - - return env.setStaticRef(staticName, value); - } - - /** - * For Reflection. - */ - public Value getStaticField(Env env, StringValue name) { - StringValue staticName = _staticFieldNameMap.get(name); - - if (staticName != null) { - return env.getStaticValue(staticName); - } else { - return null; - } - } - - // - // Constructors - // - /** - * Creates a new instance. - */ - /* - public Value callNew(Env env, Expr []args) - { - Value object = _classDef.callNew(env, args); - - if (object != null) - return object; - - object = newInstance(env); - - AbstractFunction fun = findConstructor(); - - if (fun != null) { - fun.callMethod(env, object, args); - } - - return object; - } - */ - /** - * Creates a new object without calling the constructor. This is used - * for unserializing classes. - */ - public Value createObject(Env env) { - if (_isAbstract) { - throw env.createErrorException(L.l( - "abstract class '{0}' cannot be instantiated.", - _className)); - } else if (_isInterface) { - throw env.createErrorException(L.l( - "interface '{0}' cannot be instantiated.", - _className)); - } - - ObjectValue objectValue = null; - - if (_isJavaWrapper) { - // Java objects always need to call the constructor? - return _javaClassDef.callNew(env, Value.NULL_ARGS); - } else if (_javaClassDef != null && _javaClassDef.isDelegate()) { - objectValue = new ObjectExtValue(this); - } else if (_javaClassDef != null && _javaClassDef.isPhpClass()) { - // Java objects always need to call the constructor? - Value javaWrapper = _javaClassDef.callNew(env, Value.NULL_ARGS); - Object object = javaWrapper.toJavaObject(); - - objectValue = new ObjectExtJavaValue(this, object, _javaClassDef); - } else if (_javaClassDef != null && !_javaClassDef.isDelegate()) { - objectValue = new ObjectExtJavaValue(this, null, _javaClassDef); - } else { - objectValue = _classDef.createObject(env, this); - } - - initObject(env, objectValue); - - return objectValue; - } - - /** - * Initializes the object's methods and fields. - */ - public void initObject(Env env, ObjectValue obj) { - for (int i = 0; i < _initializers.size(); i++) { - _initializers.get(i).initInstance(env, obj); - } - } - - /** - * Creates a new instance. - */ - public Value callNew(Env env, Value... args) { - QuercusClass oldCallingClass = env.setCallingClass(this); - - try { - if (_classDef.isAbstract()) { + env.addInitializedClass(map.getKey()); + } + } + + public Value getStaticFieldValue(Env env, StringValue name) { + StringValue staticName = _staticFieldNameMap.get(name); + //System.err.println("getStaticFieldValue(StringValue name) " + name); + if (staticName == null) { + env.error(L.l("{0}::${1} is an undeclared static field", + _className, name)); + + return NullValue.NULL; + } + + return env.getStaticValue(staticName); + } + + public Var getStaticFieldVar(Env env, StringValue name) { + StringValue staticName = _staticFieldNameMap.get(name); + + if (staticName == null) { + env.error(L.l("{0}::${1} is an undeclared static field", + _className, name)); + + throw new IllegalStateException(); + } + + return env.getStaticVar(staticName); + } + + public Value setStaticFieldRef(Env env, StringValue name, Value value) { + StringValue staticName = _staticFieldNameMap.get(name); + + if (staticName == null) { + env.error(L.l("{0}::{1} is an unknown static field", + _className, name)); + + throw new IllegalStateException(); + } + + return env.setStaticRef(staticName, value); + } + + /** + * For Reflection. + */ + public Value getStaticField(Env env, StringValue name) { + StringValue staticName = _staticFieldNameMap.get(name); + + if (staticName != null) { + return env.getStaticValue(staticName); + } else { + return null; + } + } + + // + // Constructors + // + /** + * Creates a new instance. + */ + /* + public Value callNew(Env env, Expr []args) + { + Value object = _classDef.callNew(env, args); + + if (object != null) + return object; + + object = newInstance(env); + + AbstractFunction fun = findConstructor(); + + if (fun != null) { + fun.callMethod(env, object, args); + } + + return object; + } + */ + /** + * Creates a new object without calling the constructor. This is used + * for unserializing classes. + */ + public Value createObject(Env env) { + if (_isAbstract) { throw env.createErrorException(L.l( "abstract class '{0}' cannot be instantiated.", _className)); - } else if (_classDef.isInterface()) { + } else if (_isInterface) { throw env.createErrorException(L.l( "interface '{0}' cannot be instantiated.", _className)); - } + } - ObjectValue objectValue = null; + ObjectValue objectValue = null; - if (_isJavaWrapper) { - return _javaClassDef.callNew(env, args); - } else if (_javaClassDef != null && _javaClassDef.isDelegate()) { + if (_isJavaWrapper) { + // Java objects always need to call the constructor? + return _javaClassDef.callNew(env, Value.NULL_ARGS); + } else if (_javaClassDef != null && _javaClassDef.isDelegate()) { objectValue = new ObjectExtValue(this); - } else if (_javaClassDef != null && _javaClassDef.isPhpClass()) { - // php/0k3- - Value javaWrapper = _javaClassDef.callNew(env, args); + } else if (_javaClassDef != null && _javaClassDef.isPhpClass()) { + // Java objects always need to call the constructor? + Value javaWrapper = _javaClassDef.callNew(env, Value.NULL_ARGS); Object object = javaWrapper.toJavaObject(); objectValue = new ObjectExtJavaValue(this, object, _javaClassDef); - } else if (_javaClassDef != null && !_javaClassDef.isDelegate()) { + } else if (_javaClassDef != null && !_javaClassDef.isDelegate()) { objectValue = new ObjectExtJavaValue(this, null, _javaClassDef); - } else { - objectValue = _classDef.newInstance(env, this); - } - - initObject(env, objectValue); - - AbstractFunction fun = findConstructor(); - - if (fun != null) { - fun.callMethod(env, this, objectValue, args); - } else { - // if expr - } - - return objectValue; - } finally { - env.setCallingClass(oldCallingClass); - } - } - - /** - * Returns the parent class. - */ - public String getParentName() { - return _classDefList[0].getParentName(); - } - - /** - * Returns true for an implementation of a class - */ - @Override - public boolean isA(String name) { - return _instanceofSet.contains(name.toLowerCase()); - } - - /* - * Returns an array of the interfaces that this class and its parents - * implements. - */ - public ArrayValue getInterfaces(Env env, boolean autoload) { - ArrayValue array = new ArrayValueImpl(); - - getInterfaces(env, array, autoload, true); - - return array; - } - - /* - * Puts the interfaces that this class and its parents implements - * into the array. - */ - private void getInterfaces(Env env, ArrayValue array, - boolean autoload, boolean isTop) { - ClassDef[] defList = _classDefList; - - for (int i = 0; i < defList.length; i++) { - ClassDef def = defList[i]; - - if (!isTop && def.isInterface()) { - String name = def.getName(); - - array.put(name, name); - } - - String[] defNames = def.getInterfaces(); - - for (int j = 0; j < defNames.length; j++) { - QuercusClass cls = env.findClass(defNames[j]); - - cls.getInterfaces(env, array, autoload, false); - } - } - - if (_parent != null) { - _parent.getInterfaces(env, array, autoload, false); - } - } - - /* - * Returns true if this class or its parents implements specified interface. - */ - public boolean implementsInterface(Env env, String name) { - ClassDef[] defList = _classDefList; - - for (int i = 0; i < defList.length; i++) { - ClassDef def = defList[i]; - - if (def.isInterface() && def.getName().equals(name)) { - return true; - } + } else { + objectValue = _classDef.createObject(env, this); + } + + initObject(env, objectValue); + + return objectValue; + } + + /** + * Initializes the object's methods and fields. + */ + public void initObject(Env env, ObjectValue obj) { + for (int i = 0; i < _initializers.size(); i++) { + _initializers.get(i).initInstance(env, obj); + } + } + + /** + * Creates a new instance. + */ + public Value callNew(Env env, Value... args) { + QuercusClass oldCallingClass = env.setCallingClass(this); + + try { + if (_classDef.isAbstract()) { + throw env.createErrorException(L.l( + "abstract class '{0}' cannot be instantiated.", + _className)); + } else if (_classDef.isInterface()) { + throw env.createErrorException(L.l( + "interface '{0}' cannot be instantiated.", + _className)); + } - String[] defNames = def.getInterfaces(); + ObjectValue objectValue = null; - for (int j = 0; j < defNames.length; j++) { - QuercusClass cls = env.findClass(defNames[j]); + if (_isJavaWrapper) { + return _javaClassDef.callNew(env, args); + } else if (_javaClassDef != null && _javaClassDef.isDelegate()) { + objectValue = new ObjectExtValue(this); + } else if (_javaClassDef != null && _javaClassDef.isPhpClass()) { + // php/0k3- + Value javaWrapper = _javaClassDef.callNew(env, args); + Object object = javaWrapper.toJavaObject(); - if (cls.implementsInterface(env, name)) { - return true; + objectValue = new ObjectExtJavaValue(this, object, _javaClassDef); + } else if (_javaClassDef != null && !_javaClassDef.isDelegate()) { + objectValue = new ObjectExtJavaValue(this, null, _javaClassDef); + } else { + objectValue = _classDef.newInstance(env, this); } - } - } - - if (_parent != null) { - return _parent.implementsInterface(env, name); - } else { - return false; - } - } - - /** - * Finds the matching constructor. - */ - public AbstractFunction findConstructor() { - return _constructor; - } - - // - // Fields - // - /** - * Implements the __get method call. - * __get() is utilized for reading data from inaccessible properties. - */ - public Value getField(Env env, Value qThis, StringValue name) { - // php/09km, php/09kn - // push/pop to prevent infinite recursion - if (issetField(name) && _fieldMap.get(name).isPublic()) { - Value v_current = this.get(name); // TODO: move to ObjectExtValue if possible - - if (v_current != NullValue.NULL && v_current != UnsetValue.UNSET) { - return v_current; - } - if (_fieldGet == null) { - return ((ClassField) _fieldMap.get(name)).getInitValue().eval(env); - } - } - - if (_fieldGet != null) { - if (!env.pushFieldGet(Env.OVERLOADING_TYPES.FIELDGET, qThis.getClassName(), name)) { - return UnsetValue.UNSET; - } - - try { - return _fieldGet.callMethod(env, this, qThis, name); - } finally { - env.popFieldGet(Env.OVERLOADING_TYPES.FIELDGET); - } - } else { - return UnsetValue.UNSET; - } - } - - /** - * Implements the __isset method call. - * __isset() is triggered by calling isset() or empty() on inaccessible properties. - */ - public Value issetField(Env env, Value qThis, StringValue name) { - if (issetField(name) && _fieldMap.get(name).isPublic()) { - Value field = qThis.getField(env, name); - if (field != null && field.isset()) { - return BooleanValue.TRUE; // TODO: move to ObjectExtValue if possible - } - } - - // basically a copy of the __get code with slightly different semantics - if (_isset != null) { - if (!env.pushFieldGet(Env.OVERLOADING_TYPES.ISSET, qThis.getClassName(), name)) { - return UnsetValue.UNSET; - } - - try { - return _isset.callMethod(env, this, qThis, name); - } finally { - env.popFieldGet(Env.OVERLOADING_TYPES.ISSET); - } - } else { - return UnsetValue.UNSET; - } - } - - @Override - public boolean issetField(StringValue name) { - if (_fieldMap.containsKey(name)) { - return true; - } - return false; - } - - @Override - public void unsetField(StringValue name) { - if (_fieldMap.containsKey(name)) { - _fieldMap.remove(name); - } - } - - /** - * implements the __unset method call - * __unset() is invoked when unset() is used on inaccessible properties. - */ - public Value unsetField(Env env, Value qThis, StringValue name) { - if (issetField(name) && _fieldMap.get(name).isPublic()) { - // TODO: move to ObjectExtValue if possible - unsetField(name); - return NullValue.NULL; - } - - // basically a copy of the __get code with slightly different semantics - if (_unset != null) { - if (!env.pushFieldGet(Env.OVERLOADING_TYPES.UNSET, qThis.getClassName(), name)) { + + initObject(env, objectValue); + + AbstractFunction fun = findConstructor(); + + if (fun != null) { + fun.callMethod(env, this, objectValue, args); + } else { + // if expr + } + + return objectValue; + } finally { + env.setCallingClass(oldCallingClass); + } + } + + /** + * Returns the parent class. + */ + public String getParentName() { + return _classDefList[0].getParentName(); + } + + /** + * Returns true for an implementation of a class + */ + @Override + public boolean isA(String name) { + return _instanceofSet.contains(name.toLowerCase()); + } + + /* + * Returns an array of the interfaces that this class and its parents + * implements. + */ + public ArrayValue getInterfaces(Env env, boolean autoload) { + ArrayValue array = new ArrayValueImpl(); + + getInterfaces(env, array, autoload, true); + + return array; + } + + /* + * Puts the interfaces that this class and its parents implements + * into the array. + */ + private void getInterfaces(Env env, ArrayValue array, + boolean autoload, boolean isTop) { + ClassDef[] defList = _classDefList; + + for (int i = 0; i < defList.length; i++) { + ClassDef def = defList[i]; + + if (!isTop && def.isInterface()) { + String name = def.getName(); + + array.put(name, name); + } + + String[] defNames = def.getInterfaces(); + + for (int j = 0; j < defNames.length; j++) { + QuercusClass cls = env.findClass(defNames[j]); + + cls.getInterfaces(env, array, autoload, false); + } + } + + if (_parent != null) { + _parent.getInterfaces(env, array, autoload, false); + } + } + + /* + * Returns true if this class or its parents implements specified interface. + */ + public boolean implementsInterface(Env env, String name) { + ClassDef[] defList = _classDefList; + + for (int i = 0; i < defList.length; i++) { + ClassDef def = defList[i]; + + if (def.isInterface() && def.getName().equals(name)) { + return true; + } + + String[] defNames = def.getInterfaces(); + + for (int j = 0; j < defNames.length; j++) { + QuercusClass cls = env.findClass(defNames[j]); + + if (cls.implementsInterface(env, name)) { + return true; + } + } + } + + if (_parent != null) { + return _parent.implementsInterface(env, name); + } else { + return false; + } + } + + /** + * Finds the matching constructor. + */ + public AbstractFunction findConstructor() { + return _constructor; + } + + // + // Fields + // + /** + * Implements the __get method call. + * __get() is utilized for reading data from inaccessible properties. + */ + public Value getField(Env env, Value qThis, StringValue name) { + // php/09km, php/09kn + // push/pop to prevent infinite recursion + if (issetField(name) && _fieldMap.get(name).isPublic()) { + Value v_current = this.get(name); // TODO: move to ObjectExtValue if possible + + if (v_current != NullValue.NULL && v_current != UnsetValue.UNSET) { + return v_current; + } + if (_fieldGet == null) { + return ((ClassField) _fieldMap.get(name)).getInitValue().eval(env); + } + } + + if (_fieldGet != null) { + if (!env.pushFieldGet(Env.OVERLOADING_TYPES.FIELDGET, qThis.getClassName(), name)) { + return UnsetValue.UNSET; + } + + try { + return _fieldGet.callMethod(env, this, qThis, name); + } finally { + env.popFieldGet(Env.OVERLOADING_TYPES.FIELDGET); + } + } else { return UnsetValue.UNSET; - } - - try { - return _unset.callMethod(env, this, qThis, name); - } finally { - env.popFieldGet(Env.OVERLOADING_TYPES.UNSET); - } - } else { - unsetField(name); - } - - return NullValue.NULL; - } - - /** - * Implements the __set method call. - */ - public Value setField(Env env, Value qThis, StringValue name, Value value) { - if (_fieldSet != null) { - if (!env.pushFieldGet(Env.OVERLOADING_TYPES.FIELDSET, qThis.getClassName(), name)) { + } + } + + /** + * Implements the __isset method call. + * __isset() is triggered by calling isset() or empty() on inaccessible properties. + */ + public Value issetField(Env env, Value qThis, StringValue name) { + if (issetField(name) && _fieldMap.get(name).isPublic()) { + Value field = qThis.getField(env, name); + if (field != null && field.isset()) { + return BooleanValue.TRUE; // TODO: move to ObjectExtValue if possible + } + } + + // basically a copy of the __get code with slightly different semantics + if (_isset != null) { + if (!env.pushFieldGet(Env.OVERLOADING_TYPES.ISSET, qThis.getClassName(), name)) { + return UnsetValue.UNSET; + } + + try { + return _isset.callMethod(env, this, qThis, name); + } finally { + env.popFieldGet(Env.OVERLOADING_TYPES.ISSET); + } + } else { return UnsetValue.UNSET; - } - - try { - return _fieldSet.callMethod(env, this, qThis, name, value); - } finally { - env.popFieldGet(Env.OVERLOADING_TYPES.FIELDSET); - } - } - return UnsetValue.UNSET; - } - - /** - * Finds the matching function. - */ - public AbstractFunction findStaticFunction(String name) { - return findFunction(name); - } - - /** - * Finds the matching function. - */ - public final AbstractFunction getFunction(StringValue methodName) { - return _methodMap.get(methodName, methodName.hashCodeCaseInsensitive()); - } - - /** - * Finds the matching function. - */ - @Override - public final AbstractFunction findFunction(String methodName) { - return _methodMap.getRaw(new StringBuilderValue(methodName)); - } - - /** - * Finds the matching function. - */ - public final AbstractFunction findFunction(StringValue methodName) { - return _methodMap.getRaw(methodName); - } - - /** - * Finds the matching function. - */ - public final AbstractFunction getFunction(StringValue methodName, int hash) { - return _methodMap.get(methodName, methodName.hashCode()); - - /* - AbstractFunction fun = _methodMap.get(methodName, hash); - - if (fun != null) - return fun; - else if (_className.equalsIgnoreCase(toMethod(name, nameLen)) - && _parent != null) { - // php/093j - return _parent.getFunction(_parent.getName()); - } - else { - throw new QuercusRuntimeException(L.l("{0}::{1} is an unknown method", - getName(), toMethod(name, nameLen))); - } - */ - } - - /** - * calls the function. - */ - public Value callMethod(Env env, - Value qThis, - StringValue methodName, int hash, - Value[] args) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethod(env, this, qThis, args); - } - - public final Value callMethod(Env env, Value qThis, StringValue methodName, - Value[] args) { - return callMethod(env, qThis, - methodName, methodName.hashCodeCaseInsensitive(), - args); - } - - /** - * calls the function. - */ - public Value callMethod(Env env, Value qThis, - StringValue methodName, int hash) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethod(env, this, qThis); - } - - public final Value callMethod(Env env, Value qThis, StringValue methodName) { - return callMethod(env, qThis, - methodName, methodName.hashCodeCaseInsensitive()); - } - - /** - * calls the function. - */ - public Value callMethod(Env env, Value qThis, - StringValue methodName, int hash, - Value a1) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethod(env, this, qThis, a1); - } - - public final Value callMethod(Env env, Value qThis, StringValue methodName, - Value a1) { - return callMethod(env, qThis, - methodName, methodName.hashCodeCaseInsensitive(), - a1); - } - - /** - * calls the function. - */ - public Value callMethod(Env env, Value qThis, - StringValue methodName, int hash, - Value a1, Value a2) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethod(env, this, qThis, a1, a2); - } - - public final Value callMethod(Env env, Value qThis, StringValue methodName, - Value a1, Value a2) { - return callMethod(env, qThis, - methodName, methodName.hashCodeCaseInsensitive(), - a1, a2); - } - - /** - * calls the function. - */ - public Value callMethod(Env env, Value qThis, - StringValue methodName, int hash, - Value a1, Value a2, Value a3) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethod(env, this, qThis, a1, a2, a3); - } - - public final Value callMethod(Env env, Value qThis, StringValue methodName, - Value a1, Value a2, Value a3) { - return callMethod(env, qThis, - methodName, methodName.hashCodeCaseInsensitive(), - a1, a2, a3); - } - - /** - * calls the function. - */ - public Value callMethod(Env env, Value qThis, - StringValue methodName, int hash, - Value a1, Value a2, Value a3, Value a4) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethod(env, this, qThis, a1, a2, a3, a4); - } - - public final Value callMethod(Env env, Value qThis, StringValue methodName, - Value a1, Value a2, Value a3, Value a4) { - return callMethod(env, qThis, - methodName, methodName.hashCodeCaseInsensitive(), - a1, a2, a3, a4); - } - - /** - * calls the function. - */ - public Value callMethod(Env env, Value qThis, - StringValue methodName, int hash, - Value a1, Value a2, Value a3, Value a4, Value a5) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethod(env, this, qThis, a1, a2, a3, a4, a5); - } - - public final Value callMethod(Env env, Value qThis, StringValue methodName, - Value a1, Value a2, Value a3, Value a4, - Value a5) { - return callMethod(env, qThis, - methodName, methodName.hashCodeCaseInsensitive(), - a1, a2, a3, a4, a5); - } - - /** - * calls the function. - */ - public Value callMethodRef(Env env, Value qThis, - StringValue methodName, int hash, - Value[] args) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethodRef(env, this, qThis, args); - } - - public final Value callMethodRef(Env env, Value qThis, StringValue methodName, - Value[] args) { - return callMethodRef(env, qThis, - methodName, methodName.hashCodeCaseInsensitive(), - args); - } - - /** - * calls the function. - */ - public Value callMethodRef(Env env, Value qThis, - StringValue methodName, int hash) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethodRef(env, this, qThis); - } - - public final Value callMethodRef(Env env, Value qThis, StringValue methodName) { - return callMethodRef(env, qThis, - methodName, methodName.hashCodeCaseInsensitive()); - } - - /** - * calls the function. - */ - public Value callMethodRef(Env env, Value qThis, - StringValue methodName, int hash, - Value a1) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethodRef(env, this, qThis, a1); - } - - public final Value callMethodRef(Env env, Value qThis, StringValue methodName, - Value a1) { - return callMethodRef(env, qThis, - methodName, methodName.hashCodeCaseInsensitive(), - a1); - } - - /** - * calls the function. - */ - public Value callMethodRef(Env env, Value qThis, - StringValue methodName, int hash, - Value a1, Value a2) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethodRef(env, this, qThis, a1, a2); - } - - public final Value callMethodRef(Env env, Value qThis, StringValue methodName, - Value a1, Value a2) { - return callMethodRef(env, qThis, - methodName, methodName.hashCodeCaseInsensitive(), - a1, a2); - } - - /** - * calls the function. - */ - public Value callMethodRef(Env env, Value qThis, - StringValue methodName, int hash, - Value a1, Value a2, Value a3) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethodRef(env, this, qThis, a1, a2, a3); - } - - public final Value callMethodRef(Env env, Value qThis, StringValue methodName, - Value a1, Value a2, Value a3) { - return callMethodRef(env, qThis, - methodName, methodName.hashCodeCaseInsensitive(), - a1, a2, a3); - } - - /** - * calls the function. - */ - public Value callMethodRef(Env env, Value qThis, - StringValue methodName, int hash, - Value a1, Value a2, Value a3, Value a4) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethodRef(env, this, qThis, - a1, a2, a3, a4); - } - - public final Value callMethodRef(Env env, Value qThis, StringValue methodName, - Value a1, Value a2, Value a3, Value a4) { - return callMethodRef(env, qThis, - methodName, methodName.hashCodeCaseInsensitive(), - a1, a2, a3, a4); - } - - /** - * calls the function. - */ - public Value callMethodRef(Env env, Value qThis, - StringValue methodName, int hash, - Value a1, Value a2, Value a3, Value a4, Value a5) { - if (qThis.isNull()) { - qThis = this; - } - - AbstractFunction fun = _methodMap.get(methodName, hash); - - return fun.callMethodRef(env, this, qThis, - a1, a2, a3, a4, a5); - } - - public final Value callMethodRef(Env env, Value qThis, StringValue methodName, - Value a1, Value a2, Value a3, Value a4, - Value a5) { - return callMethodRef(env, qThis, - methodName, methodName.hashCodeCaseInsensitive(), - a1, a2, a3, a4, a5); - } - - // - // Static method calls - // - /** - * calls the function. - */ - /* - private Value callStaticMethod(Env env, - Value thisValue, - StringValue methodName, - Expr []args) - { - QuercusClass oldClass = env.setCallingClass(this); - - try { - return callMethod(env, thisValue, methodName, args); - } finally { - env.setCallingClass(oldClass); - } - }*/ - /** - * calls the function. - */ - @Override - public Value callMethod(Env env, - StringValue methodName, int hash, - Value[] args) { - return callMethod(env, this, methodName, hash, args); - } - - /** - * calls the function. - */ - @Override - public Value callMethod(Env env, - StringValue methodName, int hash) { - return callMethod(env, this, methodName, hash); - } - - /** - * calls the function. - */ - @Override - public Value callMethod(Env env, - StringValue methodName, int hash, - Value a1) { - return callMethod(env, this, methodName, hash, - a1); - } - - /** - * calls the function. - */ - @Override - public Value callMethod(Env env, - StringValue methodName, int hash, - Value a1, Value a2) { - return callMethod(env, this, methodName, hash, - a1, a2); - } - - /** - * calls the function. - */ - @Override - public Value callMethod(Env env, - StringValue methodName, int hash, - Value a1, Value a2, Value a3) { - return callMethod(env, this, methodName, hash, - a1, a2, a3); - } - - /** - * calls the function. - */ - @Override - public Value callMethod(Env env, - StringValue methodName, int hash, - Value a1, Value a2, Value a3, Value a4) { - return callMethod(env, this, methodName, hash, - a1, a2, a3, a4); - } - - /** - * calls the function. - */ - @Override - public Value callMethod(Env env, - StringValue methodName, int hash, - Value a1, Value a2, Value a3, Value a4, - Value a5) { - return callMethod(env, this, methodName, hash, - a1, a2, a3, a4, a5); - } - - /** - * calls the function. - */ - @Override - public Value callMethodRef(Env env, - StringValue methodName, int hash, - Value[] args) { - return callMethodRef(env, this, methodName, hash, args); - } - - /** - * calls the function. - */ - @Override - public Value callMethodRef(Env env, - StringValue methodName, int hash) { - return callMethodRef(env, this, methodName, hash); - } - - /** - * calls the function. - */ - @Override - public Value callMethodRef(Env env, - StringValue methodName, int hash, - Value a1) { - return callMethodRef(env, this, methodName, hash, - a1); - } - - /** - * calls the function. - */ - @Override - public Value callMethodRef(Env env, - StringValue methodName, int hash, - Value a1, Value a2) { - return callMethodRef(env, this, methodName, hash, - a1, a2); - } - - /** - * calls the function. - */ - @Override - public Value callMethodRef(Env env, - StringValue methodName, int hash, - Value a1, Value a2, Value a3) { - return callMethodRef(env, this, methodName, hash, - a1, a2, a3); - } - - /** - * calls the function. - */ - @Override - public Value callMethodRef(Env env, - StringValue methodName, int hash, - Value a1, Value a2, Value a3, Value a4) { - return callMethodRef(env, this, methodName, hash, - a1, a2, a3, a4); - } - - /** - * calls the function. - */ - @Override - public Value callMethodRef(Env env, - StringValue methodName, int hash, - Value a1, Value a2, Value a3, Value a4, - Value a5) { - return callMethodRef(env, this, methodName, hash, - a1, a2, a3, a4, a5); - } - - private String toMethod(char[] key, int keyLength) { - return new String(key, 0, keyLength); - } - - /** - * Finds a function. - */ - public AbstractFunction findStaticFunctionLowerCase(String name) { - return null; - } - - /** - * Finds the matching function. - */ - public final AbstractFunction getStaticFunction(String name) { - AbstractFunction fun = findStaticFunction(name); - /* - if (fun != null) - return fun; - - fun = findStaticFunctionLowerCase(name.toLowerCase()); - */ - - if (fun != null) { - return fun; - } else { - throw new QuercusRuntimeException(L.l("{0}::{1} is an unknown method", - getName(), name)); - } - } - - /** - * Finds the matching constant - */ - public final Value getConstant(Env env, String name) { - Expr expr = _constMap.get(name); - - if (expr != null) { - return expr.eval(env); - } - - Object obj = _constJavaMap.get(name); - - if (obj != null) { - return env.wrapJava(obj); - } - - throw new QuercusRuntimeException(L.l("{0}::{1} is an unknown constant", - getName(), name)); - } - - /** - * Returns true if the constant exists. - */ - public final boolean hasConstant(String name) { - if (_constMap.get(name) != null) { - return true; - } else { - return _constJavaMap.get(name) != null; - } - } - - /** - * Returns the constants defined in this class. - */ - public final HashMap getConstantMap(Env env) { - HashMap map = new HashMap(); - - for (Map.Entry entry : _constMap.entrySet()) { - map.put(entry.getKey(), entry.getValue().eval(env)); - } - - for (Map.Entry entry : _constJavaMap.entrySet()) { - map.put(entry.getKey(), env.wrapJava(entry.getValue())); - } - - return map; - } - - // - // Value methods - // - @Override - public boolean isNull() { - return false; - } - - /** - * Returns the value's class name. - */ - @Override - public String getClassName() { - return getName(); - } - - @Override - public QuercusClass getQuercusClass() { - return this; - } - - @Override - public int hashCode() { - return _className.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } else if (!(o instanceof QuercusClass)) { - return false; - } - - QuercusClass qClass = (QuercusClass) o; - - if (_classDef != qClass._classDef) { - return false; - } - - if (_javaClassDef != qClass._javaClassDef) { - return false; - } - - if (_parent == qClass._parent) { - return true; - } else { - return (_parent != null && _parent.equals(qClass._parent)); - } - } - - @Override - public String toString() { - return getClass().getSimpleName() + "[" + getName() + "]"; - } - - static class StaticField { - - String _name; - Expr _expr; - - StaticField(String name, Expr expr) { - _name = name; - _expr = expr; - } - - String getName() { - return _name; - } - } - - public void setIsset(AbstractFunction isset) { - _isset = isset; - } - - public void setUnset(AbstractFunction unset) { - _unset = unset; - } - - public AbstractFunction getIsset() { - return _isset; - } - - public AbstractFunction getUnset() { - return _unset; - } + } + } + + @Override + public boolean issetField(StringValue name) { + if (_fieldMap.containsKey(name)) { + return true; + } + return false; + } + + @Override + public void unsetField(StringValue name) { + if (_fieldMap.containsKey(name)) { + _fieldMap.remove(name); + } + } + + /** + * implements the __unset method call + * __unset() is invoked when unset() is used on inaccessible properties. + */ + public Value unsetField(Env env, Value qThis, StringValue name) { + if (issetField(name) && _fieldMap.get(name).isPublic()) { + // TODO: move to ObjectExtValue if possible + unsetField(name); + return NullValue.NULL; + } + + // basically a copy of the __get code with slightly different semantics + if (_unset != null) { + if (!env.pushFieldGet(Env.OVERLOADING_TYPES.UNSET, qThis.getClassName(), name)) { + return UnsetValue.UNSET; + } + + try { + return _unset.callMethod(env, this, qThis, name); + } finally { + env.popFieldGet(Env.OVERLOADING_TYPES.UNSET); + } + } else { + unsetField(name); + } + + return NullValue.NULL; + } + + /** + * Implements the __set method call. + */ + public Value setField(Env env, Value qThis, StringValue name, Value value) { + if (_fieldSet != null) { + if (!env.pushFieldGet(Env.OVERLOADING_TYPES.FIELDSET, qThis.getClassName(), name)) { + return UnsetValue.UNSET; + } + + try { + return _fieldSet.callMethod(env, this, qThis, name, value); + } finally { + env.popFieldGet(Env.OVERLOADING_TYPES.FIELDSET); + } + } + return UnsetValue.UNSET; + } + + /** + * Finds the matching function. + */ + public AbstractFunction findStaticFunction(String name) { + return findFunction(name); + } + + /** + * Finds the matching function. + */ + public final AbstractFunction getFunction(StringValue methodName) { + return _methodMap.get(methodName, methodName.hashCodeCaseInsensitive()); + } + + /** + * Finds the matching function. + */ + @Override + public final AbstractFunction findFunction(String methodName) { + return _methodMap.getRaw(new StringBuilderValue(methodName)); + } + + /** + * Finds the matching function. + */ + public final AbstractFunction findFunction(StringValue methodName) { + return _methodMap.getRaw(methodName); + } + + /** + * Finds the matching function. + */ + public final AbstractFunction getFunction(StringValue methodName, int hash) { + return _methodMap.get(methodName, methodName.hashCode()); + + /* + AbstractFunction fun = _methodMap.get(methodName, hash); + + if (fun != null) + return fun; + else if (_className.equalsIgnoreCase(toMethod(name, nameLen)) + && _parent != null) { + // php/093j + return _parent.getFunction(_parent.getName()); + } + else { + throw new QuercusRuntimeException(L.l("{0}::{1} is an unknown method", + getName(), toMethod(name, nameLen))); + } + */ + } + + /** + * calls the function. + */ + public Value callMethod(Env env, + Value qThis, + StringValue methodName, int hash, + Value[] args) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethod(env, this, qThis, args); + } + + public final Value callMethod(Env env, Value qThis, StringValue methodName, + Value[] args) { + return callMethod(env, qThis, + methodName, methodName.hashCodeCaseInsensitive(), + args); + } + + /** + * calls the function. + */ + public Value callMethod(Env env, Value qThis, + StringValue methodName, int hash) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethod(env, this, qThis); + } + + public final Value callMethod(Env env, Value qThis, StringValue methodName) { + return callMethod(env, qThis, + methodName, methodName.hashCodeCaseInsensitive()); + } + + /** + * calls the function. + */ + public Value callMethod(Env env, Value qThis, + StringValue methodName, int hash, + Value a1) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethod(env, this, qThis, a1); + } + + public final Value callMethod(Env env, Value qThis, StringValue methodName, + Value a1) { + return callMethod(env, qThis, + methodName, methodName.hashCodeCaseInsensitive(), + a1); + } + + /** + * calls the function. + */ + public Value callMethod(Env env, Value qThis, + StringValue methodName, int hash, + Value a1, Value a2) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethod(env, this, qThis, a1, a2); + } + + public final Value callMethod(Env env, Value qThis, StringValue methodName, + Value a1, Value a2) { + return callMethod(env, qThis, + methodName, methodName.hashCodeCaseInsensitive(), + a1, a2); + } + + /** + * calls the function. + */ + public Value callMethod(Env env, Value qThis, + StringValue methodName, int hash, + Value a1, Value a2, Value a3) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethod(env, this, qThis, a1, a2, a3); + } + + public final Value callMethod(Env env, Value qThis, StringValue methodName, + Value a1, Value a2, Value a3) { + return callMethod(env, qThis, + methodName, methodName.hashCodeCaseInsensitive(), + a1, a2, a3); + } + + /** + * calls the function. + */ + public Value callMethod(Env env, Value qThis, + StringValue methodName, int hash, + Value a1, Value a2, Value a3, Value a4) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethod(env, this, qThis, a1, a2, a3, a4); + } + + public final Value callMethod(Env env, Value qThis, StringValue methodName, + Value a1, Value a2, Value a3, Value a4) { + return callMethod(env, qThis, + methodName, methodName.hashCodeCaseInsensitive(), + a1, a2, a3, a4); + } + + /** + * calls the function. + */ + public Value callMethod(Env env, Value qThis, + StringValue methodName, int hash, + Value a1, Value a2, Value a3, Value a4, Value a5) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethod(env, this, qThis, a1, a2, a3, a4, a5); + } + + public final Value callMethod(Env env, Value qThis, StringValue methodName, + Value a1, Value a2, Value a3, Value a4, + Value a5) { + return callMethod(env, qThis, + methodName, methodName.hashCodeCaseInsensitive(), + a1, a2, a3, a4, a5); + } + + /** + * calls the function. + */ + public Value callMethodRef(Env env, Value qThis, + StringValue methodName, int hash, + Value[] args) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethodRef(env, this, qThis, args); + } + + public final Value callMethodRef(Env env, Value qThis, StringValue methodName, + Value[] args) { + return callMethodRef(env, qThis, + methodName, methodName.hashCodeCaseInsensitive(), + args); + } + + /** + * calls the function. + */ + public Value callMethodRef(Env env, Value qThis, + StringValue methodName, int hash) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethodRef(env, this, qThis); + } + + public final Value callMethodRef(Env env, Value qThis, StringValue methodName) { + return callMethodRef(env, qThis, + methodName, methodName.hashCodeCaseInsensitive()); + } + + /** + * calls the function. + */ + public Value callMethodRef(Env env, Value qThis, + StringValue methodName, int hash, + Value a1) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethodRef(env, this, qThis, a1); + } + + public final Value callMethodRef(Env env, Value qThis, StringValue methodName, + Value a1) { + return callMethodRef(env, qThis, + methodName, methodName.hashCodeCaseInsensitive(), + a1); + } + + /** + * calls the function. + */ + public Value callMethodRef(Env env, Value qThis, + StringValue methodName, int hash, + Value a1, Value a2) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethodRef(env, this, qThis, a1, a2); + } + + public final Value callMethodRef(Env env, Value qThis, StringValue methodName, + Value a1, Value a2) { + return callMethodRef(env, qThis, + methodName, methodName.hashCodeCaseInsensitive(), + a1, a2); + } + + /** + * calls the function. + */ + public Value callMethodRef(Env env, Value qThis, + StringValue methodName, int hash, + Value a1, Value a2, Value a3) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethodRef(env, this, qThis, a1, a2, a3); + } + + public final Value callMethodRef(Env env, Value qThis, StringValue methodName, + Value a1, Value a2, Value a3) { + return callMethodRef(env, qThis, + methodName, methodName.hashCodeCaseInsensitive(), + a1, a2, a3); + } + + /** + * calls the function. + */ + public Value callMethodRef(Env env, Value qThis, + StringValue methodName, int hash, + Value a1, Value a2, Value a3, Value a4) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethodRef(env, this, qThis, + a1, a2, a3, a4); + } + + public final Value callMethodRef(Env env, Value qThis, StringValue methodName, + Value a1, Value a2, Value a3, Value a4) { + return callMethodRef(env, qThis, + methodName, methodName.hashCodeCaseInsensitive(), + a1, a2, a3, a4); + } + + /** + * calls the function. + */ + public Value callMethodRef(Env env, Value qThis, + StringValue methodName, int hash, + Value a1, Value a2, Value a3, Value a4, Value a5) { + if (qThis.isNull()) { + qThis = this; + } + + AbstractFunction fun = _methodMap.get(methodName, hash); + + return fun.callMethodRef(env, this, qThis, + a1, a2, a3, a4, a5); + } + + public final Value callMethodRef(Env env, Value qThis, StringValue methodName, + Value a1, Value a2, Value a3, Value a4, + Value a5) { + return callMethodRef(env, qThis, + methodName, methodName.hashCodeCaseInsensitive(), + a1, a2, a3, a4, a5); + } + + // + // Static method calls + // + /** + * calls the function. + */ + /* + private Value callStaticMethod(Env env, + Value thisValue, + StringValue methodName, + Expr []args) + { + QuercusClass oldClass = env.setCallingClass(this); + + try { + return callMethod(env, thisValue, methodName, args); + } finally { + env.setCallingClass(oldClass); + } + }*/ + /** + * calls the function. + */ + @Override + public Value callMethod(Env env, + StringValue methodName, int hash, + Value[] args) { + return callMethod(env, this, methodName, hash, args); + } + + /** + * calls the function. + */ + @Override + public Value callMethod(Env env, + StringValue methodName, int hash) { + return callMethod(env, this, methodName, hash); + } + + /** + * calls the function. + */ + @Override + public Value callMethod(Env env, + StringValue methodName, int hash, + Value a1) { + return callMethod(env, this, methodName, hash, + a1); + } + + /** + * calls the function. + */ + @Override + public Value callMethod(Env env, + StringValue methodName, int hash, + Value a1, Value a2) { + return callMethod(env, this, methodName, hash, + a1, a2); + } + + /** + * calls the function. + */ + @Override + public Value callMethod(Env env, + StringValue methodName, int hash, + Value a1, Value a2, Value a3) { + return callMethod(env, this, methodName, hash, + a1, a2, a3); + } + + /** + * calls the function. + */ + @Override + public Value callMethod(Env env, + StringValue methodName, int hash, + Value a1, Value a2, Value a3, Value a4) { + return callMethod(env, this, methodName, hash, + a1, a2, a3, a4); + } + + /** + * calls the function. + */ + @Override + public Value callMethod(Env env, + StringValue methodName, int hash, + Value a1, Value a2, Value a3, Value a4, + Value a5) { + return callMethod(env, this, methodName, hash, + a1, a2, a3, a4, a5); + } + + /** + * calls the function. + */ + @Override + public Value callMethodRef(Env env, + StringValue methodName, int hash, + Value[] args) { + return callMethodRef(env, this, methodName, hash, args); + } + + /** + * calls the function. + */ + @Override + public Value callMethodRef(Env env, + StringValue methodName, int hash) { + return callMethodRef(env, this, methodName, hash); + } + + /** + * calls the function. + */ + @Override + public Value callMethodRef(Env env, + StringValue methodName, int hash, + Value a1) { + return callMethodRef(env, this, methodName, hash, + a1); + } + + /** + * calls the function. + */ + @Override + public Value callMethodRef(Env env, + StringValue methodName, int hash, + Value a1, Value a2) { + return callMethodRef(env, this, methodName, hash, + a1, a2); + } + + /** + * calls the function. + */ + @Override + public Value callMethodRef(Env env, + StringValue methodName, int hash, + Value a1, Value a2, Value a3) { + return callMethodRef(env, this, methodName, hash, + a1, a2, a3); + } + + /** + * calls the function. + */ + @Override + public Value callMethodRef(Env env, + StringValue methodName, int hash, + Value a1, Value a2, Value a3, Value a4) { + return callMethodRef(env, this, methodName, hash, + a1, a2, a3, a4); + } + + /** + * calls the function. + */ + @Override + public Value callMethodRef(Env env, + StringValue methodName, int hash, + Value a1, Value a2, Value a3, Value a4, + Value a5) { + return callMethodRef(env, this, methodName, hash, + a1, a2, a3, a4, a5); + } + + private String toMethod(char[] key, int keyLength) { + return new String(key, 0, keyLength); + } + + /** + * Finds a function. + */ + public AbstractFunction findStaticFunctionLowerCase(String name) { + return null; + } + + /** + * Finds the matching function. + */ + public final AbstractFunction getStaticFunction(String name) { + AbstractFunction fun = findStaticFunction(name); + /* + if (fun != null) + return fun; + + fun = findStaticFunctionLowerCase(name.toLowerCase()); + */ + + if (fun != null) { + return fun; + } else { + throw new QuercusRuntimeException(L.l("{0}::{1} is an unknown method", + getName(), name)); + } + } + + /** + * Finds the matching constant + */ + public final Value getConstant(Env env, String name) { + //System.err.println(name); + //System.err.println(_constMap); + Expr expr = _constMap.get(name); + + if (expr != null) { + return expr.eval(env); + } + //System.err.println(_constMap); + Object obj = _constJavaMap.get(name); + + if (obj != null) { + return env.wrapJava(obj); + } + + throw new QuercusRuntimeException(L.l("{0}::{1} is an unknown constant", + getName(), name)); + } + + /** + * Returns true if the constant exists. + */ + public final boolean hasConstant(String name) { + if (_constMap.get(name) != null) { + return true; + } else { + return _constJavaMap.get(name) != null; + } + } + + /** + * Returns the constants defined in this class. + */ + public final HashMap getConstantMap(Env env) { + HashMap map = new HashMap(); + + for (Map.Entry entry : _constMap.entrySet()) { + map.put(entry.getKey(), entry.getValue().eval(env)); + } + + for (Map.Entry entry : _constJavaMap.entrySet()) { + map.put(entry.getKey(), env.wrapJava(entry.getValue())); + } + + return map; + } + + // + // Value methods + // + @Override + public boolean isNull() { + return false; + } + + /** + * Returns the value's class name. + */ + @Override + public String getClassName() { + return getName(); + } + + @Override + public QuercusClass getQuercusClass() { + return this; + } + + @Override + public int hashCode() { + return _className.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (!(o instanceof QuercusClass)) { + return false; + } + + QuercusClass qClass = (QuercusClass) o; + + if (_classDef != qClass._classDef) { + return false; + } + + if (_javaClassDef != qClass._javaClassDef) { + return false; + } + + if (_parent == qClass._parent) { + return true; + } else { + return (_parent != null && _parent.equals(qClass._parent)); + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + getName() + "]"; + } + + static class StaticField { + + String _name; + Expr _expr; + + StaticField(String name, Expr expr) { + _name = name; + _expr = expr; + } + + String getName() { + return _name; + } + } + + public void setIsset(AbstractFunction isset) { + _isset = isset; + } + + public void setUnset(AbstractFunction unset) { + _unset = unset; + } + + public AbstractFunction getIsset() { + return _isset; + } + + public AbstractFunction getUnset() { + return _unset; + } } diff --git a/quercus/src/main/java/com/caucho/quercus/env/StringBuilderValue.java b/quercus/src/main/java/com/caucho/quercus/env/StringBuilderValue.java index 6cf2ea8..9880a2c 100644 --- a/quercus/src/main/java/com/caucho/quercus/env/StringBuilderValue.java +++ b/quercus/src/main/java/com/caucho/quercus/env/StringBuilderValue.java @@ -42,2005 +42,2044 @@ public class StringBuilderValue extends BinaryValue { - public static final StringBuilderValue EMPTY = new ConstStringValue(""); - private static final StringBuilderValue[] CHAR_STRINGS; - private static final int LARGE_BUILDER_THRESHOLD = LargeStringBuilderValue.SIZE; - private byte[] _buffer; - private int _length; - private boolean _isCopy; - private int _hashCode; + public static final StringBuilderValue EMPTY = new ConstStringValue(""); + private static final StringBuilderValue[] CHAR_STRINGS; + private static final int LARGE_BUILDER_THRESHOLD = LargeStringBuilderValue.SIZE; + private byte[] _buffer; + private int _length; + private boolean _isCopy; + private int _hashCode; - public StringBuilderValue() { - _buffer = new byte[MIN_LENGTH]; - } + public StringBuilderValue() { + _buffer = new byte[MIN_LENGTH]; + } - public StringBuilderValue(int capacity) { - if (capacity < MIN_LENGTH) { - capacity = MIN_LENGTH; - } + public StringBuilderValue(int capacity) { + if (capacity < MIN_LENGTH) { + capacity = MIN_LENGTH; + } - _buffer = new byte[capacity]; - } + _buffer = new byte[capacity]; + } - public StringBuilderValue(byte[] buffer, int offset, int length) { - _buffer = new byte[length]; - _length = length; + public StringBuilderValue(byte[] buffer, int offset, int length) { + _buffer = new byte[length]; + _length = length; - System.arraycopy(buffer, offset, _buffer, 0, length); - } + System.arraycopy(buffer, offset, _buffer, 0, length); + } - public StringBuilderValue(char[] buffer, int offset, int length) { - _buffer = new byte[length]; - _length = length; - - for (int i = 0; i < length; i++) { - _buffer[i] = (byte) buffer[offset + i]; - } - } - - /** - * Creates a new StringBuilderValue with the buffer without copying. - */ - public StringBuilderValue(char[] buffer, int length) { - this(buffer, 0, length); - } - - public StringBuilderValue(byte[] buffer) { - this(buffer, 0, buffer.length); - } - - public StringBuilderValue(char ch) { - _buffer = new byte[1]; - _length = 1; - - _buffer[0] = (byte) ch; - } - - public StringBuilderValue(byte ch) { - _buffer = new byte[1]; - _length = 1; - - _buffer[0] = ch; - } - - public StringBuilderValue(String s) { - int len = s.length(); - - _buffer = new byte[len]; - _length = len; - - for (int i = 0; i < len; i++) { - _buffer[i] = (byte) s.charAt(i); - } - } - - public StringBuilderValue(char[] s) { - this(s, 0, s.length); - } - - public StringBuilderValue(char[] s, Value v1) { - int len = s.length; - - int bufferLength = MIN_LENGTH; - while (bufferLength < len) { - bufferLength *= 2; - } - - _buffer = new byte[bufferLength]; - _length = len; - - for (int i = 0; i < len; i++) { - _buffer[i] = (byte) s[i]; - } - - v1.appendTo(this); - } - - public StringBuilderValue(byte[] s, Value v1) { - int len = s.length; - - int bufferLength = MIN_LENGTH; - while (bufferLength < len) { - bufferLength *= 2; - } - - _buffer = new byte[bufferLength]; - _length = len; - - System.arraycopy(s, 0, _buffer, 0, len); - - v1.appendTo(this); - } - - public StringBuilderValue(Value v1) { - if (v1 instanceof StringBuilderValue) { - init((StringBuilderValue) v1); - } else { - _buffer = new byte[MIN_LENGTH]; - - v1.appendTo(this); - } - } - - public StringBuilderValue(StringBuilderValue v) { - init(v); - } - - private void init(StringBuilderValue v) { - if (v._isCopy || v instanceof ConstStringValue) { - _buffer = new byte[v._buffer.length]; - System.arraycopy(v._buffer, 0, _buffer, 0, v._length); - _length = v._length; - } else { - _buffer = v._buffer; - _length = v._length; - v._isCopy = true; - } - } - - public StringBuilderValue(Value v1, Value v2) { - _buffer = new byte[MIN_LENGTH]; - - v1.appendTo(this); - v2.appendTo(this); - } - - public StringBuilderValue(Value v1, Value v2, Value v3) { - _buffer = new byte[MIN_LENGTH]; - - v1.appendTo(this); - v2.appendTo(this); - v3.appendTo(this); - } - - /** - * Creates the string. - */ - public static StringValue create(byte value) { - return CHAR_STRINGS[value & 0xFF]; - } - - /** - * Creates the string. - */ - public static StringValue create(char value) { - return CHAR_STRINGS[value & 0xFF]; - } - - /** - * Creates a PHP string from a Java String. - * If the value is null then NullValue is returned. - */ - public static Value create(String value) { - if (value == null) { - return NullValue.NULL; - } else if (value.length() == 0) { - return StringBuilderValue.EMPTY; - } else { - return new StringBuilderValue(value); - } - } - - /** - * Returns the value. - */ - public final String getValue() { - return toString(); - } - - /** - * Returns the type. - */ - @Override - public String getType() { - return "string"; - } - - /** - * Returns the ValueType. - */ - @Override - public ValueType getValueType() { - return getValueType(_buffer, 0, _length); - } - - public static ValueType getValueType(byte[] buffer, - int offset, - int len) { - if (len == 0) { - // php/0307 - return ValueType.LONG_ADD; - } - - int i = offset; - int ch = 0; - - while (i < len && Character.isWhitespace(buffer[i])) { - i++; - } - - if (i + 1 < len && buffer[i] == '0' && buffer[i + 1] == 'x') { - return ValueType.LONG_EQ; - } - - if (i < len && ((ch = buffer[i]) == '+' || ch == '-')) { - i++; - } - - if (len <= i) { - return ValueType.STRING; - } - - ch = buffer[i]; - - if (ch == '.') { - for (i++; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { - return ValueType.DOUBLE_CMP; - } - - return ValueType.STRING; - } else if (!('0' <= ch && ch <= '9')) { - return ValueType.STRING; - } - - for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { - } - - while (i < len && Character.isWhitespace(buffer[i])) { - i++; - } - - if (len <= i) { - return ValueType.LONG_EQ; - } else if (ch == '.' || ch == 'e' || ch == 'E') { - for (i++; - i < len - && ('0' <= (ch = buffer[i]) && ch <= '9' - || ch == '+' - || ch == '-' - || ch == 'e' - || ch == 'E'); - i++) { - } - - while (i < len && Character.isWhitespace(buffer[i])) { + public StringBuilderValue(char[] buffer, int offset, int length) { + _buffer = new byte[length]; + _length = length; + + for (int i = 0; i < length; i++) { + _buffer[i] = (byte) buffer[offset + i]; + } + } + + /** + * Creates a new StringBuilderValue with the buffer without copying. + */ + public StringBuilderValue(char[] buffer, int length) { + this(buffer, 0, length); + } + + public StringBuilderValue(byte[] buffer) { + this(buffer, 0, buffer.length); + } + + public StringBuilderValue(char ch) { + _buffer = new byte[1]; + _length = 1; + + _buffer[0] = (byte) ch; + } + + public StringBuilderValue(byte ch) { + _buffer = new byte[1]; + _length = 1; + + _buffer[0] = ch; + } + + public StringBuilderValue(String s) { + int len = s.length(); + + _buffer = new byte[len]; + _length = len; + + for (int i = 0; i < len; i++) { + _buffer[i] = (byte) s.charAt(i); + } + } + + public StringBuilderValue(char[] s) { + this(s, 0, s.length); + } + + public StringBuilderValue(char[] s, Value v1) { + int len = s.length; + + int bufferLength = MIN_LENGTH; + while (bufferLength < len) { + bufferLength *= 2; + } + + _buffer = new byte[bufferLength]; + _length = len; + + for (int i = 0; i < len; i++) { + _buffer[i] = (byte) s[i]; + } + + v1.appendTo(this); + } + + public StringBuilderValue(byte[] s, Value v1) { + int len = s.length; + + int bufferLength = MIN_LENGTH; + while (bufferLength < len) { + bufferLength *= 2; + } + + _buffer = new byte[bufferLength]; + _length = len; + + System.arraycopy(s, 0, _buffer, 0, len); + + v1.appendTo(this); + } + + public StringBuilderValue(Value v1) { + if (v1 instanceof StringBuilderValue) { + init((StringBuilderValue) v1); + } else { + _buffer = new byte[MIN_LENGTH]; + + v1.appendTo(this); + } + } + + public StringBuilderValue(StringBuilderValue v) { + init(v); + } + + private void init(StringBuilderValue v) { + if (v._isCopy || v instanceof ConstStringValue) { + _buffer = new byte[v._buffer.length]; + System.arraycopy(v._buffer, 0, _buffer, 0, v._length); + _length = v._length; + } else { + _buffer = v._buffer; + _length = v._length; + v._isCopy = true; + } + } + + public StringBuilderValue(Value v1, Value v2) { + _buffer = new byte[MIN_LENGTH]; + + v1.appendTo(this); + v2.appendTo(this); + } + + public StringBuilderValue(Value v1, Value v2, Value v3) { + _buffer = new byte[MIN_LENGTH]; + + v1.appendTo(this); + v2.appendTo(this); + v3.appendTo(this); + } + + /** + * Creates the string. + */ + public static StringValue create(byte value) { + return CHAR_STRINGS[value & 0xFF]; + } + + /** + * Creates the string. + */ + public static StringValue create(char value) { + return CHAR_STRINGS[value & 0xFF]; + } + + /** + * Creates a PHP string from a Java String. + * If the value is null then NullValue is returned. + */ + public static Value create(String value) { + if (value == null) { + return NullValue.NULL; + } else if (value.length() == 0) { + return StringBuilderValue.EMPTY; + } else { + return new StringBuilderValue(value); + } + } + + /** + * Returns the value. + */ + public final String getValue() { + return toString(); + } + + /** + * Returns the type. + */ + @Override + public String getType() { + return "string"; + } + + /** + * Returns the ValueType. + */ + @Override + public ValueType getValueType() { + return getValueType(_buffer, 0, _length); + } + + public static ValueType getValueType(byte[] buffer, + int offset, + int len) { + if (len == 0) { + // php/0307 + return ValueType.LONG_ADD; + } + + int i = offset; + int ch = 0; + + while (i < len && Character.isWhitespace(buffer[i])) { i++; - } + } - if (i < len) { + if (i + 1 < len && buffer[i] == '0' && buffer[i + 1] == 'x') { + return ValueType.LONG_EQ; + } + + if (i < len && ((ch = buffer[i]) == '+' || ch == '-')) { + i++; + } + + if (len <= i) { return ValueType.STRING; - } else { - return ValueType.DOUBLE_CMP; - } - } else { - return ValueType.STRING; - } - } - - /** - * Returns true for a scalar - */ - @Override - public final boolean isScalar() { - return true; - } - - /* - * Returns true if this is a PHP5 string. - */ - @Override - public boolean isPHP5String() { - return true; - } - - /** - * Converts to a boolean. - */ - @Override - public final boolean toBoolean() { - if (_length == 0) { - return false; - } else if (_length == 1 && _buffer[0] == '0') { - return false; - } else { - return true; - } - } - - /** - * Converts to a long. - */ - @Override - public long toLong() { - return parseLong(_buffer, 0, _length); - } - - /** - * Converts to a double. - */ - @Override - public double toDouble() { - return toDouble(_buffer, 0, _length); - } - - public static double toDouble(byte[] buffer, int offset, int len) { - int start = offset; - int i = offset; - int ch = 0; - - while (i < len && Character.isWhitespace(buffer[i])) { - start++; - i++; - } - - int end = offset + len; - - if (offset + 1 < end && buffer[offset] == '0' - && ((ch = buffer[offset + 1]) == 'x' || ch == 'X')) { - - double value = 0; - - for (offset += 2; offset < end; offset++) { - ch = buffer[offset] & 0xFF; + } - if ('0' <= ch && ch <= '9') { - value = value * 16 + ch - '0'; - } else if ('a' <= ch && ch <= 'z') { - value = value * 16 + ch - 'a' + 10; - } else if ('A' <= ch && ch <= 'Z') { - value = value * 16 + ch - 'A' + 10; + ch = buffer[i]; + + if (ch == '.') { + for (i++; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { + return ValueType.DOUBLE_CMP; + } + + return ValueType.STRING; + } else if (!('0' <= ch && ch <= '9')) { + return ValueType.STRING; + } + + for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { + } + + while (i < len && Character.isWhitespace(buffer[i])) { + i++; + } + + if (len <= i) { + return ValueType.LONG_EQ; + } else if (ch == '.' || ch == 'e' || ch == 'E') { + for (i++; + i < len + && ('0' <= (ch = buffer[i]) && ch <= '9' + || ch == '+' + || ch == '-' + || ch == 'e' + || ch == 'E'); + i++) { + } + + while (i < len && Character.isWhitespace(buffer[i])) { + i++; + } + + if (i < len) { + return ValueType.STRING; } else { - return value; + return ValueType.DOUBLE_CMP; + } + } else { + return ValueType.STRING; + } + } + + /** + * Returns true for a scalar + */ + @Override + public final boolean isScalar() { + return true; + } + + /* + * Returns true if this is a PHP5 string. + */ + @Override + public boolean isPHP5String() { + return true; + } + + /** + * Converts to a boolean. + */ + @Override + public final boolean toBoolean() { + if (_length == 0) { + return false; + } else if (_length == 1 && _buffer[0] == '0') { + return false; + } else { + return true; + } + } + + /** + * Converts to a long. + */ + @Override + public long toLong() { + return parseLong(_buffer, 0, _length); + } + + /** + * Converts to a double. + */ + @Override + public double toDouble() { + return toDouble(_buffer, 0, _length); + } + + public static double toDouble(byte[] buffer, int offset, int len) { + int start = offset; + int i = offset; + int ch = 0; + + while (i < len && Character.isWhitespace(buffer[i])) { + start++; + i++; + } + + int end = offset + len; + + if (offset + 1 < end && buffer[offset] == '0' + && ((ch = buffer[offset + 1]) == 'x' || ch == 'X')) { + + double value = 0; + + for (offset += 2; offset < end; offset++) { + ch = buffer[offset] & 0xFF; + + if ('0' <= ch && ch <= '9') { + value = value * 16 + ch - '0'; + } else if ('a' <= ch && ch <= 'z') { + value = value * 16 + ch - 'a' + 10; + } else if ('A' <= ch && ch <= 'Z') { + value = value * 16 + ch - 'A' + 10; + } else { + return value; + } + } + + return value; + } + + if (i < len && ((ch = buffer[i]) == '+' || ch == '-')) { + i++; + } + + for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { + } + + if (ch == '.') { + for (i++; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { } - } - return value; - } + if (i == 1) { + return 0; + } + } - if (i < len && ((ch = buffer[i]) == '+' || ch == '-')) { - i++; - } + if (ch == 'e' || ch == 'E') { + int e = i++; - for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { - } + if (i < len && (ch = buffer[i]) == '+' || ch == '-') { + i++; + } - if (ch == '.') { - for (i++; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { - } + for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { + } - if (i == 1) { + if (i == e + 1) { + i = e; + } + } + + if (i == 0) { + return 0; + } + + try { + return Double.parseDouble(new String(buffer, start, i - start)); + } catch (NumberFormatException e) { return 0; - } - } + } + } + + /** + * Convert to an input stream. + */ + @Override + public final InputStream toInputStream() { + return new BuilderInputStream(); + } + + /** + * Converts to a string. + */ + @Override + public String toString() { + if (_length == 1) { + return String.valueOf((char) (_buffer[0] & 0xFF)); + } else { + CharBuffer buf = CharBuffer.allocate(); + buf.append(_buffer, 0, _length); + + String str = buf.toString(); + buf.free(); + + return str; + } + } + + /** + * Converts to a BinaryValue. + */ + @Override + public final StringValue toBinaryValue(Env env) { + return this; + } + + /** + * Converts to a BinaryValue in desired charset. + */ + @Override + public final StringValue toBinaryValue(String charset) { + return this; + } + + /** + * Converts to a UnicodeValue. + */ + @Override + public StringValue toUnicodeValue() { + // php/0c94 + return new UnicodeBuilderValue().append(getBuffer(), 0, length()); + } + + /** + * Converts to a UnicodeValue. + */ + @Override + public StringValue toUnicodeValue(Env env) { + return toUnicodeValue(); + } + + /** + * Converts to a UnicodeValue in desired charset. + */ + @Override + public StringValue toUnicodeValue(Env env, String charset) { + return toUnicodeValue(); + } + + /** + * Converts to an object. + */ + @Override + public final Object toJavaObject() { + return toString(); + } + + /** + * Returns true if the value is empty. + */ + @Override + public final boolean isEmpty() { + return _length == 0 || _length == 1 && _buffer[0] == '0'; + } + + /** + * Writes to a stream + */ + @Override + public final void writeTo(OutputStream os) { + try { + os.write(_buffer, 0, _length); + + } catch (IOException e) { + throw new QuercusModuleException(e); + } + } + + /** + * Append to a string builder. + */ + @Override + public StringValue appendTo(StringBuilderValue bb) { + bb.append(_buffer, 0, _length); + + return bb; + } + + /** + * Append to a string builder. + */ + @Override + public StringValue appendTo(UnicodeBuilderValue bb) { + bb.append(_buffer, 0, _length); + + return bb; + } + + /** + * Append to a string builder. + */ + @Override + public StringValue appendTo(LargeStringBuilderValue bb) { + bb.append(_buffer, 0, _length); + + return bb; + } + + /** + * Append to a string builder. + */ + @Override + public StringValue appendTo(BinaryBuilderValue bb) { + bb.append(_buffer, 0, _length); + + return bb; + } + + /** + * Converts to a key. + */ + @Override + public Value toKey() { + byte[] buffer = _buffer; + int len = _length; + + if (len == 0) { + return this; + } - if (ch == 'e' || ch == 'E') { - int e = i++; + int sign = 1; + long value = 0; - if (i < len && (ch = buffer[i]) == '+' || ch == '-') { + int i = 0; + int ch = buffer[i]; + if (ch == '-') { + sign = -1; i++; - } - - for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { - } - - if (i == e + 1) { - i = e; - } - } - - if (i == 0) { - return 0; - } - - try { - return Double.parseDouble(new String(buffer, start, i - start)); - } catch (NumberFormatException e) { - return 0; - } - } - - /** - * Convert to an input stream. - */ - @Override - public final InputStream toInputStream() { - return new BuilderInputStream(); - } - - /** - * Converts to a string. - */ - @Override - public String toString() { - if (_length == 1) { - return String.valueOf((char) (_buffer[0] & 0xFF)); - } else { - CharBuffer buf = CharBuffer.allocate(); - buf.append(_buffer, 0, _length); - - String str = buf.toString(); - buf.free(); - - return str; - } - } - - /** - * Converts to a BinaryValue. - */ - @Override - public final StringValue toBinaryValue(Env env) { - return this; - } - - /** - * Converts to a BinaryValue in desired charset. - */ - @Override - public final StringValue toBinaryValue(String charset) { - return this; - } - - /** - * Converts to a UnicodeValue. - */ - @Override - public StringValue toUnicodeValue() { - // php/0c94 - return new UnicodeBuilderValue().append(getBuffer(), 0, length()); - } - - /** - * Converts to a UnicodeValue. - */ - @Override - public StringValue toUnicodeValue(Env env) { - return toUnicodeValue(); - } - - /** - * Converts to a UnicodeValue in desired charset. - */ - @Override - public StringValue toUnicodeValue(Env env, String charset) { - return toUnicodeValue(); - } - - /** - * Converts to an object. - */ - @Override - public final Object toJavaObject() { - return toString(); - } - - /** - * Returns true if the value is empty. - */ - @Override - public final boolean isEmpty() { - return _length == 0 || _length == 1 && _buffer[0] == '0'; - } - - /** - * Writes to a stream - */ - @Override - public final void writeTo(OutputStream os) { - try { - os.write(_buffer, 0, _length); - - } catch (IOException e) { - throw new QuercusModuleException(e); - } - } - - /** - * Append to a string builder. - */ - @Override - public StringValue appendTo(StringBuilderValue bb) { - bb.append(_buffer, 0, _length); - - return bb; - } - - /** - * Append to a string builder. - */ - @Override - public StringValue appendTo(UnicodeBuilderValue bb) { - bb.append(_buffer, 0, _length); - - return bb; - } - - /** - * Append to a string builder. - */ - @Override - public StringValue appendTo(LargeStringBuilderValue bb) { - bb.append(_buffer, 0, _length); - - return bb; - } - - /** - * Append to a string builder. - */ - @Override - public StringValue appendTo(BinaryBuilderValue bb) { - bb.append(_buffer, 0, _length); - - return bb; - } - - /** - * Converts to a key. - */ - @Override - public Value toKey() { - byte[] buffer = _buffer; - int len = _length; - - if (len == 0) { - return this; - } - - int sign = 1; - long value = 0; - - int i = 0; - int ch = buffer[i]; - if (ch == '-') { - sign = -1; - i++; - } - - for (; i < len; i++) { - ch = buffer[i]; - - if ('0' <= ch && ch <= '9') { - value = 10 * value + ch - '0'; - } else { + } + + for (; i < len; i++) { + ch = buffer[i]; + + if ('0' <= ch && ch <= '9') { + value = 10 * value + ch - '0'; + } else { + return this; + } + } + + return LongValue.create(sign * value); + } + + /** + * Converts to a byte array, with no consideration of character encoding. + * Each character becomes one byte, characters with values above 255 are + * not correctly preserved. + */ + @Override + public final byte[] toBytes() { + byte[] bytes = new byte[_length]; + + System.arraycopy(_buffer, 0, bytes, 0, _length); + + return bytes; + } + + // + // Operations + // + /** + * Returns the character at an index + */ + @Override + public final Value get(Value key) { + return charValueAt(key.toLong()); + } + + /** + * Sets the array ref. + */ + @Override + public Value put(Value index, Value value) { + setCharValueAt(index.toLong(), value); + + return value; + } + + /** + * Sets the array ref. + */ + @Override + public Value append(Value index, Value value) { + if (_length > 0) { + return setCharValueAt(index.toLong(), value); + } else { + return new ArrayValueImpl().append(index, value); + } + } + + /** + * sets the character at an index + */ + /* + public Value setCharAt(long index, String value) + { + int len = _length; + + if (index < 0 || len <= index) + return this; + else { + StringBuilderValue sb = new StringBuilderValue(_buffer, 0, (int) index); + sb.append(value); + sb.append(_buffer, (int) (index + 1), (int) (len - index - 1)); + + return sb; + } + } + */ + // + // CharSequence + // + /** + * Returns the length of the string. + */ + @Override + public int length() { + return _length; + } + + /** + * Returns the character at a particular location + */ + @Override + public final char charAt(int index) { + if (index < 0 || _length <= index) { + return 0; + } else { + return (char) (_buffer[index] & 0xff); + } + } + + /** + * Returns the character at an index + */ + @Override + public Value charValueAt(long index) { + int len = _length; + + if (index < 0 || len <= index) { + return UnsetStringValue.UNSET; + } else { + byte ch = _buffer[(int) index]; + + return CHAR_STRINGS[ch & 0xff]; + } + } + + /** + * sets the character at an index + */ + @Override + public Value setCharValueAt(long indexL, Value value) { + int len = _length; + + if (indexL < 0) { return this; - } - } - - return LongValue.create(sign * value); - } - - /** - * Converts to a byte array, with no consideration of character encoding. - * Each character becomes one byte, characters with values above 255 are - * not correctly preserved. - */ - @Override - public final byte[] toBytes() { - byte[] bytes = new byte[_length]; - - System.arraycopy(_buffer, 0, bytes, 0, _length); - - return bytes; - } - - // - // Operations - // - /** - * Returns the character at an index - */ - @Override - public final Value get(Value key) { - return charValueAt(key.toLong()); - } - - /** - * Sets the array ref. - */ - @Override - public Value put(Value index, Value value) { - setCharValueAt(index.toLong(), value); - - return value; - } - - /** - * Sets the array ref. - */ - @Override - public Value append(Value index, Value value) { - if (_length > 0) { - return setCharValueAt(index.toLong(), value); - } else { - return new ArrayValueImpl().append(index, value); - } - } - - /** - * sets the character at an index - */ - /* - public Value setCharAt(long index, String value) - { - int len = _length; - - if (index < 0 || len <= index) - return this; - else { - StringBuilderValue sb = new StringBuilderValue(_buffer, 0, (int) index); - sb.append(value); - sb.append(_buffer, (int) (index + 1), (int) (len - index - 1)); - - return sb; - } - } - */ - // - // CharSequence - // - /** - * Returns the length of the string. - */ - @Override - public int length() { - return _length; - } - - /** - * Returns the character at a particular location - */ - @Override - public final char charAt(int index) { - if (index < 0 || _length <= index) { - return 0; - } else { - return (char) (_buffer[index] & 0xff); - } - } - - /** - * Returns the character at an index - */ - @Override - public Value charValueAt(long index) { - int len = _length; - - if (index < 0 || len <= index) { - return UnsetStringValue.UNSET; - } else { - byte ch = _buffer[(int) index]; - - return CHAR_STRINGS[ch & 0xff]; - } - } - - /** - * sets the character at an index - */ - @Override - public Value setCharValueAt(long indexL, Value value) { - int len = _length; - - if (indexL < 0) { - return this; - } else if (indexL < len) { - StringBuilderValue sb = createStringBuilder(_buffer, 0, len); - - StringValue str = value.toStringValue(); - - int index = (int) indexL; - - if (value.length() == 0) { - sb._buffer[index] = 0; - } else { - sb._buffer[index] = (byte) str.charAt(0); - } - - return sb; - } else { - // php/03mg, #2940 - - int index = (int) indexL; - - StringBuilderValue sb = (StringBuilderValue) copyStringBuilder(); - - if (sb._buffer.length < index + 1) { - sb.ensureCapacity(index + 1); - } - - int padLen = index - len; - - for (int i = 0; i <= padLen; i++) { - sb._buffer[sb._length++] = ' '; - } - - StringValue str = value.toStringValue(); - - if (value.length() == 0) { - sb._buffer[index] = 0; - } else { - sb._buffer[index] = (byte) str.charAt(0); - } - - return sb; - } - } - - /** - * Returns the first index of the match string, starting from the head. - */ - @Override - public int indexOf(char match) { - final int length = _length; - final byte[] buffer = _buffer; - - for (int head = 0; head < length; head++) { - if (buffer[head] == match) { - return head; - } - } - - return -1; - } - - /** - * Returns the last index of the match string, starting from the head. - */ - @Override - public int indexOf(char match, int head) { - int length = _length; - byte[] buffer = _buffer; - - for (; head < length; head++) { - if (buffer[head] == match) { - return head; - } - } - - return -1; - } - - /** - * Returns a subsequence - */ - @Override - public CharSequence subSequence(int start, int end) { - if (end <= start) { - return StringBuilderValue.EMPTY; - } else if (end - start == 1) { - return CHAR_STRINGS[_buffer[start] & 0xff]; - } - - return createStringBuilder(_buffer, start, end - start); - } - - /** - * Returns a subsequence - */ - @Override - public String stringSubstring(int start, int end) { - if (end <= start) { - return ""; - } - - CharBuffer buf = CharBuffer.allocate(); - buf.append(_buffer, start, end - start); - - String str = buf.toString(); - buf.free(); - - return str; - } - - /** - * Convert to lower case. - */ - @Override - public StringValue toLowerCase() { - int length = _length; - - StringBuilderValue string = createStringBuilder(length); - - byte[] srcBuffer = _buffer; - byte[] dstBuffer = string._buffer; - - for (int i = 0; i < length; i++) { - byte ch = srcBuffer[i]; - - if ('A' <= ch && ch <= 'Z') { - dstBuffer[i] = (byte) (ch + 'a' - 'A'); - } else { - dstBuffer[i] = ch; - } - } - - string._length = length; - - return string; - } - - /** - * Convert to lower case. - */ - @Override - public StringValue toUpperCase() { - int length = _length; - - StringBuilderValue string = createStringBuilder(_length); - - byte[] srcBuffer = _buffer; - byte[] dstBuffer = string._buffer; - - for (int i = 0; i < length; i++) { - byte ch = srcBuffer[i]; - - if ('a' <= ch && ch <= 'z') { - dstBuffer[i] = (byte) (ch + 'A' - 'a'); - } else { - dstBuffer[i] = ch; - } - } - - string._length = length; - - return string; - } - - /** - * Returns true if the region matches - */ - @Override - public boolean regionMatches(int offset, - char[] mBuffer, - int mOffset, - int mLength) { - int length = _length; - - if (length < offset + mLength) { - return false; - } - - byte[] buffer = _buffer; - - for (int i = 0; i < mLength; i++) { - if ((buffer[offset + i] & 0xFF) != mBuffer[mOffset + i]) { - return false; - } - } + } else if (indexL < len) { + StringBuilderValue sb = createStringBuilder(_buffer, 0, len); + + StringValue str = value.toStringValue(); + + int index = (int) indexL; + + if (value.length() == 0) { + sb._buffer[index] = 0; + } else { + sb._buffer[index] = (byte) str.charAt(0); + } + + return sb; + } else { + // php/03mg, #2940 - return true; - } + int index = (int) indexL; + + StringBuilderValue sb = (StringBuilderValue) copyStringBuilder(); + + if (sb._buffer.length < index + 1) { + sb.ensureCapacity(index + 1); + } - /** - * Returns true if the region matches - */ - @Override - public boolean regionMatchesIgnoreCase(int offset, - char[] mBuffer, - int mOffset, - int mLength) { - int length = _length; + int padLen = index - len; + + for (int i = 0; i <= padLen; i++) { + sb._buffer[sb._length++] = ' '; + } + + StringValue str = value.toStringValue(); + + if (value.length() == 0) { + sb._buffer[index] = 0; + } else { + sb._buffer[index] = (byte) str.charAt(0); + } + + return sb; + } + } + + /** + * Returns the first index of the match string, starting from the head. + */ + @Override + public int indexOf(char match) { + final int length = _length; + final byte[] buffer = _buffer; + + for (int head = 0; head < length; head++) { + if (buffer[head] == match) { + return head; + } + } + + return -1; + } + + /** + * Returns the last index of the match string, starting from the head. + */ + @Override + public int indexOf(char match, int head) { + int length = _length; + byte[] buffer = _buffer; + + for (; head < length; head++) { + if (buffer[head] == match) { + return head; + } + } + + return -1; + } + + /** + * Returns a subsequence + */ + @Override + public CharSequence subSequence(int start, int end) { + if (end <= start) { + return StringBuilderValue.EMPTY; + } else if (end - start == 1) { + return CHAR_STRINGS[_buffer[start] & 0xff]; + } + + return createStringBuilder(_buffer, start, end - start); + } + + /** + * Returns a subsequence + */ + @Override + public String stringSubstring(int start, int end) { + if (end <= start) { + return ""; + } + + CharBuffer buf = CharBuffer.allocate(); + buf.append(_buffer, start, end - start); + + String str = buf.toString(); + buf.free(); + + return str; + } + + /** + * Convert to lower case. + */ + @Override + public StringValue toLowerCase() { + int length = _length; + + StringBuilderValue string = createStringBuilder(length); + + byte[] srcBuffer = _buffer; + byte[] dstBuffer = string._buffer; + + for (int i = 0; i < length; i++) { + byte ch = srcBuffer[i]; + + if ('A' <= ch && ch <= 'Z') { + dstBuffer[i] = (byte) (ch + 'a' - 'A'); + } else { + dstBuffer[i] = ch; + } + } - if (length < offset + mLength) { - return false; - } + string._length = length; - byte[] buffer = _buffer; + return string; + } - for (int i = 0; i < mLength; i++) { - int a = buffer[offset + i] & 0xFF; - char b = mBuffer[mOffset + i]; + /** + * Convert to lower case. + */ + @Override + public StringValue toUpperCase() { + int length = _length; - if ('A' <= a && a <= 'Z') { - a += 'a' - 'A'; - } + StringBuilderValue string = createStringBuilder(_length); - if ('A' <= b && b <= 'Z') { - b += 'a' - 'A'; - } + byte[] srcBuffer = _buffer; + byte[] dstBuffer = string._buffer; - if (a != b) { + for (int i = 0; i < length; i++) { + byte ch = srcBuffer[i]; + + if ('a' <= ch && ch <= 'z') { + dstBuffer[i] = (byte) (ch + 'A' - 'a'); + } else { + dstBuffer[i] = ch; + } + } + + string._length = length; + + return string; + } + + /** + * Returns true if the region matches + */ + @Override + public boolean regionMatches(int offset, + char[] mBuffer, + int mOffset, + int mLength) { + int length = _length; + + if (length < offset + mLength) { + return false; + } + + byte[] buffer = _buffer; + + for (int i = 0; i < mLength; i++) { + if ((buffer[offset + i] & 0xFF) != mBuffer[mOffset + i]) { + return false; + } + } + + return true; + } + + /** + * Returns true if the region matches + */ + @Override + public boolean regionMatchesIgnoreCase(int offset, + char[] mBuffer, + int mOffset, + int mLength) { + int length = _length; + + if (length < offset + mLength) { return false; - } - } - - return true; - } - - /** - * Creates a string builder of the same type. - */ - @Override - public StringBuilderValue createStringBuilder() { - return new StringBuilderValue(); - } - - /** - * Creates a string builder of the same type. - */ - @Override - public StringBuilderValue createStringBuilder(int length) { - return new StringBuilderValue(length); - } - - /** - * Creates a string builder of the same type. - */ - public StringBuilderValue createStringBuilder(byte[] buffer, int offset, int length) { - return new StringBuilderValue(buffer, offset, length); - } - - /** - * Converts to a string builder - */ - @Override - public StringValue copyStringBuilder() { - return new StringBuilderValue(this); - } - - /** - * Converts to a string builder - */ - @Override - public StringValue toStringBuilder() { - return new StringBuilderValue(this); - } - - /** - * Converts to a string builder - */ - @Override - public StringValue toStringBuilder(Env env) { - if (_length >= LARGE_BUILDER_THRESHOLD) { - return new LargeStringBuilderValue(this); - } else { - return new StringBuilderValue(this); - } - } - - /** - * Converts to a string builder - */ - @Override - public StringValue toStringBuilder(Env env, Value value) { - if (_length + value.length() >= LARGE_BUILDER_THRESHOLD) { - LargeStringBuilderValue v = new LargeStringBuilderValue(this); - - value.appendTo(v); - - return v; - } else { - StringBuilderValue v = new StringBuilderValue(this); - - value.appendTo(v); - - return v; - } - } - - /** - * Converts to a string builder - */ - @Override - public StringValue toStringBuilder(Env env, StringValue value) { - if (_length + value.length() >= LARGE_BUILDER_THRESHOLD) { - LargeStringBuilderValue v = new LargeStringBuilderValue(this); - - value.appendTo(v); - - return v; - } else { - StringBuilderValue v = new StringBuilderValue(this); - - value.appendTo(v); - - return v; - } - } - - // - // append code - // - /** - * Append a Java string to the value. - */ - @Override - public final StringValue append(String s) { - int sublen = s.length(); - - if (_buffer.length < _length + sublen) { - ensureCapacity(_length + sublen); - } - - byte[] buffer = _buffer; - int length = _length; - - for (int i = 0; i < sublen; i++) { - buffer[length + i] = (byte) s.charAt(i); - } - - _length = length + sublen; - - return this; - } - - /** - * Append a Java string to the value. - */ - @Override - public final StringValue append(String s, int start, int end) { - int sublen = end - start; - - if (_buffer.length < _length + sublen) { - ensureCapacity(_length + sublen); - } - - byte[] buffer = _buffer; - int length = _length; - - for (; start < end; start++) { - buffer[length++] = (byte) s.charAt(start); - } - - _length = length; - - return this; - } - - /** - * Append a Java char to the value. - */ - @Override - public final StringValue append(char ch) { - if (_buffer.length < _length + 1) { - ensureCapacity(_length + 1); - } - - _buffer[_length++] = (byte) ch; - - return this; - } - - @Override - public final void write(int ch) { - if (_buffer.length < _length + 1) { - ensureCapacity(_length + 1); - } - - _buffer[_length++] = (byte) ch; - } - - /** - * Append a Java buffer to the value. - */ - @Override - public final StringValue append(char[] buf, int offset, int length) { - int end = _length + length; - - if (_buffer.length < end) { - ensureCapacity(end); - } + } + + byte[] buffer = _buffer; + + for (int i = 0; i < mLength; i++) { + int a = buffer[offset + i] & 0xFF; + char b = mBuffer[mOffset + i]; + + if ('A' <= a && a <= 'Z') { + a += 'a' - 'A'; + } + + if ('A' <= b && b <= 'Z') { + b += 'a' - 'A'; + } + + if (a != b) { + return false; + } + } + + return true; + } + + /** + * Creates a string builder of the same type. + */ + @Override + public StringBuilderValue createStringBuilder() { + return new StringBuilderValue(); + } + + /** + * Creates a string builder of the same type. + */ + @Override + public StringBuilderValue createStringBuilder(int length) { + return new StringBuilderValue(length); + } + + /** + * Creates a string builder of the same type. + */ + public StringBuilderValue createStringBuilder(byte[] buffer, int offset, int length) { + return new StringBuilderValue(buffer, offset, length); + } + + /** + * Converts to a string builder + */ + @Override + public StringValue copyStringBuilder() { + return new StringBuilderValue(this); + } + + /** + * Converts to a string builder + */ + @Override + public StringValue toStringBuilder() { + return new StringBuilderValue(this); + } + + /** + * Converts to a string builder + */ + @Override + public StringValue toStringBuilder(Env env) { + if (_length >= LARGE_BUILDER_THRESHOLD) { + return new LargeStringBuilderValue(this); + } else { + return new StringBuilderValue(this); + } + } + + /** + * Converts to a string builder + */ + @Override + public StringValue toStringBuilder(Env env, Value value) { + if (_length + value.length() >= LARGE_BUILDER_THRESHOLD) { + LargeStringBuilderValue v = new LargeStringBuilderValue(this); + + value.appendTo(v); + + return v; + } else { + StringBuilderValue v = new StringBuilderValue(this); + + value.appendTo(v); + + return v; + } + } + + /** + * Converts to a string builder + */ + @Override + public StringValue toStringBuilder(Env env, StringValue value) { + if (_length + value.length() >= LARGE_BUILDER_THRESHOLD) { + LargeStringBuilderValue v = new LargeStringBuilderValue(this); + + value.appendTo(v); + + return v; + } else { + StringBuilderValue v = new StringBuilderValue(this); + + value.appendTo(v); + + return v; + } + } + + // + // append code + // + /** + * Append a Java string to the value. + */ + @Override + public final StringValue append(String s) { + int sublen = s.length(); + + if (_buffer.length < _length + sublen) { + ensureCapacity(_length + sublen); + } + + byte[] buffer = _buffer; + int length = _length; + + for (int i = 0; i < sublen; i++) { + buffer[length + i] = (byte) s.charAt(i); + } + + _length = length + sublen; + + return this; + } + + /** + * Append a Java string to the value. + */ + @Override + public final StringValue append(String s, int start, int end) { + int sublen = end - start; + + if (_buffer.length < _length + sublen) { + ensureCapacity(_length + sublen); + } + + byte[] buffer = _buffer; + int length = _length; + + for (; start < end; start++) { + buffer[length++] = (byte) s.charAt(start); + } + + _length = length; + + return this; + } + + /** + * Append a Java char to the value. + */ + @Override + public final StringValue append(char ch) { + if (_buffer.length < _length + 1) { + ensureCapacity(_length + 1); + } + + _buffer[_length++] = (byte) ch; + + return this; + } + + @Override + public final void write(int ch) { + if (_buffer.length < _length + 1) { + ensureCapacity(_length + 1); + } + + _buffer[_length++] = (byte) ch; + } + + /** + * Append a Java buffer to the value. + */ + @Override + public final StringValue append(char[] buf, int offset, int length) { + int end = _length + length; + + if (_buffer.length < end) { + ensureCapacity(end); + } + + byte[] buffer = _buffer; + int bufferLength = _length; + + for (int i = 0; i < length; i++) { + buffer[bufferLength + i] = (byte) buf[offset + i]; + } + + _length = end; - byte[] buffer = _buffer; - int bufferLength = _length; + return this; + } - for (int i = 0; i < length; i++) { - buffer[bufferLength + i] = (byte) buf[offset + i]; - } + /** + * Append a Java buffer to the value. + */ + @Override + public final StringValue append(char[] buf) { + int length = buf.length; - _length = end; + if (_buffer.length < _length + length) { + ensureCapacity(_length + length); + } - return this; - } + byte[] buffer = _buffer; + int bufferLength = _length; - /** - * Append a Java buffer to the value. - */ - @Override - public final StringValue append(char[] buf) { - int length = buf.length; - - if (_buffer.length < _length + length) { - ensureCapacity(_length + length); - } + for (int i = 0; i < length; i++) { + buffer[bufferLength++] = (byte) buf[i]; + } - byte[] buffer = _buffer; - int bufferLength = _length; + _buffer = buffer; + _length = bufferLength; - for (int i = 0; i < length; i++) { - buffer[bufferLength++] = (byte) buf[i]; - } + return this; + } - _buffer = buffer; - _length = bufferLength; + /** + * Append a Java buffer to the value. + */ + @Override + public StringValue appendUnicode(char[] buf) { + int length = buf.length; - return this; - } + if (_buffer.length < _length + length) { + ensureCapacity(_length + length); + } - /** - * Append a Java buffer to the value. - */ - @Override - public StringValue appendUnicode(char[] buf) { - int length = buf.length; - - if (_buffer.length < _length + length) { - ensureCapacity(_length + length); - } - - byte[] buffer = _buffer; - int bufferLength = _length; + byte[] buffer = _buffer; + int bufferLength = _length; - for (int i = 0; i < length; i++) { - buffer[bufferLength++] = (byte) buf[i]; - } + for (int i = 0; i < length; i++) { + buffer[bufferLength++] = (byte) buf[i]; + } - _buffer = buffer; - _length = bufferLength; + _buffer = buffer; + _length = bufferLength; - return this; - } - - /** - * Append a Java buffer to the value. - */ - @Override - public StringValue appendUnicode(char[] buf, int offset, int length) { - if (_buffer.length < _length + length) { - ensureCapacity(_length + length); - } - - byte[] buffer = _buffer; - int bufferLength = _length; - - for (; length > 0; length--) { - buffer[bufferLength++] = (byte) buf[offset++]; - } - - _buffer = buffer; - _length = bufferLength; - - return this; - } - - /** - * Append a Java buffer to the value. - */ - @Override - public final StringValue append(CharSequence buf, int head, int tail) { - int length = tail - head; - - if (_buffer.length < _length + length) { - ensureCapacity(_length + length); - } - - if (buf instanceof StringBuilderValue) { - StringBuilderValue sb = (StringBuilderValue) buf; - - System.arraycopy(sb._buffer, head, _buffer, _length, length); - - _length += length; - - return this; - } else { - byte[] buffer = _buffer; - int bufferLength = _length; - - for (; head < tail; head++) { - buffer[bufferLength++] = (byte) buf.charAt(head); - } - - _length = bufferLength; - - return this; - } - } - - /** - * Append a Java buffer to the value. - */ - @Override - public StringValue append(StringBuilderValue sb, int head, int tail) { - int length = tail - head; - - if (_buffer.length < _length + length) { - ensureCapacity(_length + length); - } - - System.arraycopy(sb._buffer, head, _buffer, _length, length); - - _length += length; - - return this; - } - - /** - * Append a Java value to the value. - */ - @Override - public final StringValue append(Value v) { - /* - if (v.length() == 0) - return this; - else { - // php/033a - v.appendTo(this); - - return this; - } - */ - - v.appendTo(this); - - return this; - } - - /** - * Returns the first index of the match string, starting from the head. - */ - @Override - public final int indexOf(CharSequence match, int head) { - final int matchLength = match.length(); - - if (matchLength <= 0) { - return -1; - } else if (head < 0) { - return -1; - } - - final int length = _length; - final int end = length - matchLength; - final char first = match.charAt(0); - final byte[] buffer = _buffer; - - loop: - for (; head <= end; head++) { - if (buffer[head] != first) { - continue; - } - - for (int i = 1; i < matchLength; i++) { - if (buffer[head + i] != match.charAt(i)) { - continue loop; + return this; + } + + /** + * Append a Java buffer to the value. + */ + @Override + public StringValue appendUnicode(char[] buf, int offset, int length) { + if (_buffer.length < _length + length) { + ensureCapacity(_length + length); + } + + byte[] buffer = _buffer; + int bufferLength = _length; + + for (; length > 0; length--) { + buffer[bufferLength++] = (byte) buf[offset++]; + } + + _buffer = buffer; + _length = bufferLength; + + return this; + } + + /** + * Append a Java buffer to the value. + */ + @Override + public final StringValue append(CharSequence buf, int head, int tail) { + int length = tail - head; + + if (_buffer.length < _length + length) { + ensureCapacity(_length + length); + } + + if (buf instanceof StringBuilderValue) { + StringBuilderValue sb = (StringBuilderValue) buf; + + System.arraycopy(sb._buffer, head, _buffer, _length, length); + + _length += length; + + return this; + } else { + byte[] buffer = _buffer; + int bufferLength = _length; + + for (; head < tail; head++) { + buffer[bufferLength++] = (byte) buf.charAt(head); } - } - return head; - } - - return -1; - } - - /** - * Append a Java value to the value. - */ - @Override - public StringValue appendUnicode(Value v) { - v.appendTo(this); - - return this; - } - - /** - * Append a Java value to the value. - */ - @Override - public StringValue appendUnicode(Value v1, Value v2) { - v1.appendTo(this); - v2.appendTo(this); - - return this; - } - - /** - * Append a buffer to the value. - */ - @Override - public final StringValue append(byte[] buf, int offset, int length) { - int end = _length + length; - - if (_buffer.length < end) { - ensureCapacity(end); - } - - System.arraycopy(buf, offset, _buffer, _length, length); - - _length = end; - - return this; - } - - @Override - public final void write(byte[] buf, int offset, int length) { - append(buf, offset, length); - } - - /** - * Append a double to the value. - */ - @Override - public final StringValue append(byte[] buf) { - return append(buf, 0, buf.length); - } - - /** - * Append a buffer to the value. - */ - @Override - public final StringValue appendUtf8(byte[] buf, int offset, int length) { - if (_buffer.length < _length + length) { - ensureCapacity(_length + length); - } - - byte[] charBuffer = _buffer; - int charLength = _length; - - int end = offset + length; - - while (offset < end) { - int ch = buf[offset++] & 0xff; - - if (ch < 0x80) { - charBuffer[charLength++] = (byte) ch; - } else if (ch < 0xe0) { - int ch2 = buf[offset++] & 0xff; - - int v = (char) (((ch & 0x1f) << 6) + (ch2 & 0x3f)); - - charBuffer[charLength++] = (byte) (v & 0xff); - } else { - int ch2 = buf[offset++] & 0xff; - int ch3 = buf[offset++] & 0xff; - - byte v = (byte) (((ch & 0xf) << 12) - + ((ch2 & 0x3f) << 6) - + ((ch3) << 6)); - - charBuffer[charLength++] = v; - } - } - - _length = charLength; - - return this; - } - - /** - * Append a Java byte to the value without conversions. - */ - @Override - public final StringValue appendByte(int v) { - if (_buffer.length < _length + 1) { - ensureCapacity(_length + 1); - } - - _buffer[_length++] = (byte) v; - - return this; - } - - /** - * Append a Java boolean to the value. - */ - @Override - public final StringValue append(boolean v) { - return append(v ? "true" : "false"); - } - - /** - * Append a Java long to the value. - */ - @Override - public StringValue append(long v) { - return append(String.valueOf(v)); - } - - /** - * Append a Java double to the value. - */ - @Override - public StringValue append(double v) { - return append(String.valueOf(v)); - } - - /** - * Append a bytes to the value. - */ - @Override - public StringValue appendBytes(String s) { - int sublen = s.length(); - - if (_buffer.length < _length + sublen) { - ensureCapacity(_length + sublen); - } - - for (int i = 0; i < sublen; i++) { - _buffer[_length++] = (byte) s.charAt(i); - } - - return this; - } - - /** - * Append Java bytes to the value without conversions. - */ - @Override - public final StringValue appendBytes(byte[] bytes, int offset, int end) { - int len = end - offset; - - if (_buffer.length < _length + len) { - ensureCapacity(_length + len); - } + _length = bufferLength; + + return this; + } + } + + /** + * Append a Java buffer to the value. + */ + @Override + public StringValue append(StringBuilderValue sb, int head, int tail) { + int length = tail - head; + + if (_buffer.length < _length + length) { + ensureCapacity(_length + length); + } + + System.arraycopy(sb._buffer, head, _buffer, _length, length); + + _length += length; + + return this; + } + + /** + * Append a Java value to the value. + */ + @Override + public final StringValue append(Value v) { + /* + if (v.length() == 0) + return this; + else { + // php/033a + v.appendTo(this); + + return this; + } + */ + + v.appendTo(this); + + return this; + } + + /** + * Returns the first index of the match string, starting from the head. + */ + @Override + public final int indexOf(CharSequence match, int head) { + final int matchLength = match.length(); + + if (matchLength <= 0) { + return -1; + } else if (head < 0) { + return -1; + } + + final int length = _length; + final int end = length - matchLength; + final char first = match.charAt(0); + final byte[] buffer = _buffer; - System.arraycopy(bytes, offset, _buffer, _length, len); - - _length += len; - - return this; - } - - @Override - public StringValue append(Reader reader, long length) - throws IOException { - // php/4407 - oracle clob callback passes very long length - - TempCharBuffer tempBuf = TempCharBuffer.allocate(); - - char[] buffer = tempBuf.getBuffer(); - - int sublen = (int) Math.min(buffer.length, length); - - try { - while (length > 0) { - if (_buffer.length < _length + sublen) { - ensureCapacity(_length + sublen); + loop: + for (; head <= end; head++) { + if (buffer[head] != first) { + continue; } - int count = reader.read(buffer, 0, sublen); + for (int i = 1; i < matchLength; i++) { + if (buffer[head + i] != match.charAt(i)) { + continue loop; + } + } + + return head; + } + + return -1; + } + + /** + * Append a Java value to the value. + */ + @Override + public StringValue appendUnicode(Value v) { + v.appendTo(this); + + return this; + } + + /** + * Append a Java value to the value. + */ + @Override + public StringValue appendUnicode(Value v1, Value v2) { + v1.appendTo(this); + v2.appendTo(this); + + return this; + } + + /** + * Append a buffer to the value. + */ + @Override + public final StringValue append(byte[] buf, int offset, int length) { + int end = _length + length; + + if (_buffer.length < end) { + ensureCapacity(end); + } + + System.arraycopy(buf, offset, _buffer, _length, length); + + _length = end; + + return this; + } + + @Override + public final void write(byte[] buf, int offset, int length) { + append(buf, offset, length); + } + + /** + * Append a double to the value. + */ + @Override + public final StringValue append(byte[] buf) { + return append(buf, 0, buf.length); + } + + /** + * Append a buffer to the value. + */ + @Override + public final StringValue appendUtf8(byte[] buf, int offset, int length) { + if (_buffer.length < _length + length) { + ensureCapacity(_length + length); + } + + byte[] charBuffer = _buffer; + int charLength = _length; + + int end = offset + length; + + while (offset < end) { + int ch = buf[offset++] & 0xff; + + if (ch < 0x80) { + charBuffer[charLength++] = (byte) ch; + } else if (ch < 0xe0) { + int ch2 = buf[offset++] & 0xff; + + int v = (char) (((ch & 0x1f) << 6) + (ch2 & 0x3f)); + + charBuffer[charLength++] = (byte) (v & 0xff); + } else { + int ch2 = buf[offset++] & 0xff; + int ch3 = buf[offset++] & 0xff; + + byte v = (byte) (((ch & 0xf) << 12) + + ((ch2 & 0x3f) << 6) + + ((ch3) << 6)); - if (count <= 0) { - break; + charBuffer[charLength++] = v; } + } + + _length = charLength; + + return this; + } - append(buffer, 0, count); - - length -= count; - } - - } catch (IOException e) { - throw new QuercusModuleException(e); - } finally { - TempCharBuffer.free(tempBuf); - } - - return this; - } - - /** - * Returns the buffer. - */ - public final byte[] getBuffer() { - return _buffer; - } - - /** - * Returns the offset. - */ - public int getOffset() { - return _length; - } - - /** - * Sets the offset. - */ - public void setOffset(int offset) { - _length = offset; - } - - /** - * Returns the current capacity. - */ - public int getBufferLength() { - return _buffer.length; - } - - /** - * Return true if the array value is set - */ - @Override - public boolean isset(Value indexV) { - int index = indexV.toInt(); - - return 0 <= index && index < _length; - } - - // - // Java generator code - // - /** - * Prints the value. - * @param env - */ - @Override - public void print(Env env) { - env.write(_buffer, 0, _length); - } - - /** - * Prints the value. - * @param env - */ - @Override - public void print(Env env, WriteStream out) { - try { - out.write(_buffer, 0, _length); - } catch (IOException e) { - throw new QuercusModuleException(e); - } - } - - /** - * Serializes the value. - */ - @Override - public void serialize(Env env, StringBuilder sb) { - sb.append("s:"); - sb.append(_length); - sb.append(":\""); - - for (int i = 0; i < _length; i++) { - sb.append((char) (_buffer[i] & 0xFF)); - } - - sb.append("\";"); - } - - @Override - public String toDebugString() { - StringBuilder sb = new StringBuilder(); - - int length = length(); - - sb.append("binary("); - sb.append(length); - sb.append(") \""); - - int appendLength = length < 256 ? length : 256; - - for (int i = 0; i < appendLength; i++) { - sb.append(charAt(i)); - } - - if (length > 256) { - sb.append(" ..."); - } - - sb.append('"'); - - return sb.toString(); - } - - @Override - public void varDumpImpl(Env env, - WriteStream out, - int depth, - IdentityHashMap valueSet) - throws IOException { - int length = length(); - - if (length < 0) { - length = 0; - } - - out.print("string("); - out.print(length); - out.print(") \""); - - out.write(_buffer, 0, _length); - - out.print("\""); - } - - /** - * Returns an OutputStream. - */ - public OutputStream getOutputStream() { - return new BuilderOutputStream(); - } - - /** - * Calculates CRC32 value. - */ - @Override - public long getCrc32Value() { - CRC32 crc = new CRC32(); - - crc.update(_buffer, 0, _length); - - return crc.getValue() & 0xffffffff; - } - - @Override - public void ensureAppendCapacity(int newCapacity) { - ensureCapacity(_length + newCapacity); - } - - protected void ensureCapacity(int newCapacity) { - int bufferLength = _buffer.length; - - if (newCapacity <= bufferLength) { - return; - } - - if (bufferLength < MIN_LENGTH) { - bufferLength = MIN_LENGTH; - } - - while (bufferLength <= newCapacity) { - bufferLength = 2 * bufferLength; - } - - byte[] buffer = new byte[bufferLength]; - System.arraycopy(_buffer, 0, buffer, 0, _length); - _buffer = buffer; - _isCopy = false; - } + /** + * Append a Java byte to the value without conversions. + */ + @Override + public final StringValue appendByte(int v) { + if (_buffer.length < _length + 1) { + ensureCapacity(_length + 1); + } - /** - * Returns the hash code. - */ - @Override - public int hashCode() { - int hash = _hashCode; + _buffer[_length++] = (byte) v; - if (hash != 0) { - return hash; - } + return this; + } - hash = 37; + /** + * Append a Java boolean to the value. + */ + @Override + public final StringValue append(boolean v) { + return append(v ? "true" : "false"); + } - int length = _length; - byte[] buffer = _buffer; + /** + * Append a Java long to the value. + */ + @Override + public StringValue append(long v) { + return append(String.valueOf(v)); + } - if (length > 256) { - for (int i = 127; i >= 0; i--) { - hash = 65521 * hash + buffer[i]; - } + /** + * Append a Java double to the value. + */ + @Override + public StringValue append(double v) { + return append(String.valueOf(v)); + } + + /** + * Append a bytes to the value. + */ + @Override + public StringValue appendBytes(String s) { + int sublen = s.length(); + + if (_buffer.length < _length + sublen) { + ensureCapacity(_length + sublen); + } + + for (int i = 0; i < sublen; i++) { + _buffer[_length++] = (byte) s.charAt(i); + } + + return this; + } + + /** + * Append Java bytes to the value without conversions. + */ + @Override + public final StringValue appendBytes(byte[] bytes, int offset, int end) { + int len = end - offset; + + if (_buffer.length < _length + len) { + ensureCapacity(_length + len); + } + + System.arraycopy(bytes, offset, _buffer, _length, len); + + _length += len; + + return this; + } + + @Override + public StringValue append(Reader reader, long length) + throws IOException { + // php/4407 - oracle clob callback passes very long length + + TempCharBuffer tempBuf = TempCharBuffer.allocate(); + + char[] buffer = tempBuf.getBuffer(); + + int sublen = (int) Math.min(buffer.length, length); + + try { + while (length > 0) { + if (_buffer.length < _length + sublen) { + ensureCapacity(_length + sublen); + } + + int count = reader.read(buffer, 0, sublen); + + if (count <= 0) { + break; + } + + append(buffer, 0, count); + + length -= count; + } + + } catch (IOException e) { + throw new QuercusModuleException(e); + } finally { + TempCharBuffer.free(tempBuf); + } + + return this; + } + + /** + * Returns the buffer. + */ + public final byte[] getBuffer() { + return _buffer; + } + + /** + * Returns the offset. + */ + public int getOffset() { + return _length; + } + + /** + * Sets the offset. + */ + public void setOffset(int offset) { + _length = offset; + } + + /** + * Returns the current capacity. + */ + public int getBufferLength() { + return _buffer.length; + } + + /** + * Return true if the array value is set + */ + @Override + public boolean isset(Value indexV) { + int index = indexV.toInt(); + + return 0 <= index && index < _length; + } + + // + // Java generator code + // + /** + * Prints the value. + * @param env + */ + @Override + public void print(Env env) { + env.write(_buffer, 0, _length); + } + + /** + * Prints the value. + * @param env + */ + @Override + public void print(Env env, WriteStream out) { + try { + out.write(_buffer, 0, _length); + } catch (IOException e) { + throw new QuercusModuleException(e); + } + } + + /** + * Serializes the value. + */ + @Override + public void serialize(Env env, StringBuilder sb) { + sb.append("s:"); + sb.append(_length); + sb.append(":\""); + + for (int i = 0; i < _length; i++) { + sb.append((char) (_buffer[i] & 0xFF)); + } + + sb.append("\";"); + } + + @Override + public String toDebugString() { + StringBuilder sb = new StringBuilder(); + + int length = length(); + + sb.append("binary("); + sb.append(length); + sb.append(") \""); + + int appendLength = length < 256 ? length : 256; + + for (int i = 0; i < appendLength; i++) { + sb.append(charAt(i)); + } + + if (length > 256) { + sb.append(" ..."); + } + + sb.append('"'); + + return sb.toString(); + } + + @Override + public void varDumpImpl(Env env, + WriteStream out, + int depth, + IdentityHashMap valueSet) + throws IOException { + int length = length(); + + if (length < 0) { + length = 0; + } + + out.print("string("); + out.print(length); + out.print(") \""); + + out.write(_buffer, 0, _length); + + out.print("\""); + } + + /** + * Returns an OutputStream. + */ + public OutputStream getOutputStream() { + return new BuilderOutputStream(); + } + + /** + * Calculates CRC32 value. + */ + @Override + public long getCrc32Value() { + CRC32 crc = new CRC32(); + + crc.update(_buffer, 0, _length); + + return crc.getValue() & 0xffffffff; + } + + @Override + public void ensureAppendCapacity(int newCapacity) { + ensureCapacity(_length + newCapacity); + } + + protected void ensureCapacity(int newCapacity) { + int bufferLength = _buffer.length; + + if (newCapacity <= bufferLength) { + return; + } + + if (bufferLength < MIN_LENGTH) { + bufferLength = MIN_LENGTH; + } + + while (bufferLength <= newCapacity) { + bufferLength = 2 * bufferLength; + } + + byte[] buffer = new byte[bufferLength]; + System.arraycopy(_buffer, 0, buffer, 0, _length); + _buffer = buffer; + _isCopy = false; + } + + /** + * Returns the hash code. + */ + @Override + public int hashCode() { + int hash = _hashCode; + + if (hash != 0) { + //System.err.println("StringBuilderValue Hash code for 1" + this + " = " + hash); + return hash; + } + + hash = 37; - for (int i = length - 128; i < length; i++) { + int length = _length; + byte[] buffer = _buffer; + + if (length > 256) { + for (int i = 127; i >= 0; i--) { + hash = 65521 * hash + buffer[i]; + } + + for (int i = length - 128; i < length; i++) { + hash = 65521 * hash + buffer[i]; + } + + _hashCode = hash; + //System.err.println("StringBuilderValue Hash code for 2" + this + " = " + hash); + return hash; + } + + for (int i = length - 1; i >= 0; i--) { hash = 65521 * hash + buffer[i]; - } + } - _hashCode = hash; + _hashCode = hash; + // System.err.println("StringBuilderValue Hash code for 3" + this + " = " + hash); + return hash; + } - return hash; - } + /** + * Returns the hash code. + */ + @Override + public int hashCodeCaseInsensitive() { + int hash = 0; - for (int i = length - 1; i >= 0; i--) { - hash = 65521 * hash + buffer[i]; - } + if (hash != 0) { + return hash; + } - _hashCode = hash; + hash = 37; - return hash; - } + int length = _length; + byte[] buffer = _buffer; - /** - * Returns the hash code. - */ - @Override - public int hashCodeCaseInsensitive() { - int hash = 0; + if (length > 256) { + for (int i = 127; i >= 0; i--) { + hash = 65521 * hash + toLower(buffer[i]); + } - if (hash != 0) { - return hash; - } + for (int i = length - 128; i < length; i++) { + hash = 65521 * hash + toLower(buffer[i]); + } - hash = 37; + _hashCode = hash; - int length = _length; - byte[] buffer = _buffer; + return hash; + } - if (length > 256) { - for (int i = 127; i >= 0; i--) { - hash = 65521 * hash + toLower(buffer[i]); - } + for (int i = length - 1; i >= 0; i--) { + int ch = toLower(buffer[i]); - for (int i = length - 128; i < length; i++) { - hash = 65521 * hash + toLower(buffer[i]); - } + hash = 65521 * hash + ch; + } - _hashCode = hash; + return hash; + } - return hash; - } + private int toLower(int ch) { + if ('A' <= ch && ch <= 'Z') { + return ch + 'a' - 'A'; + } else { + return ch; + } + } - for (int i = length - 1; i >= 0; i--) { - int ch = toLower(buffer[i]); + @Override + public int getHashCode() { + return hashCode(); + } - hash = 65521 * hash + ch; - } + /** + * Returns true for equality + */ + @Override + public boolean eq(Value rValue) { + rValue = rValue.toValue(); - return hash; - } + ValueType typeB = rValue.getValueType(); - private int toLower(int ch) { - if ('A' <= ch && ch <= 'Z') { - return ch + 'a' - 'A'; - } else { - return ch; - } - } + if (typeB.isNumber()) { + double l = toDouble(); + double r = rValue.toDouble(); - @Override - public int getHashCode() { - return hashCode(); - } + return l == r; + } else if (typeB.isBoolean()) { + return toBoolean() == rValue.toBoolean(); + } - /** - * Returns true for equality - */ - @Override - public boolean eq(Value rValue) { - rValue = rValue.toValue(); + ValueType typeA = getValueType(); + if (typeA.isNumberCmp() && typeB.isNumberCmp()) { + double l = toDouble(); + double r = rValue.toDouble(); - ValueType typeB = rValue.getValueType(); + return l == r; + } - if (typeB.isNumber()) { - double l = toDouble(); - double r = rValue.toDouble(); + if (rValue instanceof StringBuilderValue) { + StringBuilderValue value = (StringBuilderValue) rValue; - return l == r; - } else if (typeB.isBoolean()) { - return toBoolean() == rValue.toBoolean(); - } + int length = _length; - ValueType typeA = getValueType(); - if (typeA.isNumberCmp() && typeB.isNumberCmp()) { - double l = toDouble(); - double r = rValue.toDouble(); + if (length != value._length) { + return false; + } - return l == r; - } + byte[] bufferA = _buffer; + byte[] bufferB = value._buffer; - if (rValue instanceof StringBuilderValue) { - StringBuilderValue value = (StringBuilderValue) rValue; + for (int i = length - 1; i >= 0; i--) { + if (bufferA[i] != bufferB[i]) { + return false; + } + } - int length = _length; + return true; + } else { + String rString = rValue.toString(); - if (length != value._length) { - return false; - } + int len = rString.length(); - byte[] bufferA = _buffer; - byte[] bufferB = value._buffer; + if (_length != len) { + return false; + } - for (int i = length - 1; i >= 0; i--) { - if (bufferA[i] != bufferB[i]) { - return false; + for (int i = len - 1; i >= 0; i--) { + if (_buffer[i] != rString.charAt(i)) { + return false; + } } - } - return true; - } else { - String rString = rValue.toString(); + return true; + } + } - int len = rString.length(); + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } - if (_length != len) { - return false; - } + if (o instanceof StringBuilderValue) { + StringBuilderValue value = (StringBuilderValue) o; - for (int i = len - 1; i >= 0; i--) { - if (_buffer[i] != rString.charAt(i)) { - return false; + int length = _length; + + if (length != value._length) { + return false; } - } - return true; - } - } + byte[] bufferA = _buffer; + byte[] bufferB = value._buffer; - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } + for (int i = length - 1; i >= 0; i--) { + if (bufferA[i] != bufferB[i]) { + return false; + } + } - if (o instanceof StringBuilderValue) { - StringBuilderValue value = (StringBuilderValue) o; + return true; + } else if (o instanceof LargeStringBuilderValue) { + StringValue value = (StringValue) o; - int length = _length; + int length = _length; + int lengthB = value.length(); - if (length != value._length) { - return false; - } + if (length != lengthB) { + return false; + } - byte[] bufferA = _buffer; - byte[] bufferB = value._buffer; + byte[] bufferA = _buffer; - for (int i = length - 1; i >= 0; i--) { - if (bufferA[i] != bufferB[i]) { - return false; + for (int i = length - 1; i >= 0; i--) { + if (bufferA[i] != value.charAt(i)) { + return false; + } } - } - return true; - } else if (o instanceof LargeStringBuilderValue) { - StringValue value = (StringValue) o; + return true; + } else if (o instanceof UnicodeBuilderValue) { + UnicodeBuilderValue value = (UnicodeBuilderValue) o; - int length = _length; - int lengthB = value.length(); + int length = _length; + int lengthB = value.length(); - if (length != lengthB) { - return false; - } + if (length != lengthB) { + return false; + } - byte[] bufferA = _buffer; + byte[] bufferA = _buffer; - for (int i = length - 1; i >= 0; i--) { - if (bufferA[i] != value.charAt(i)) { - return false; + for (int i = length - 1; i >= 0; i--) { + if (bufferA[i] != value.charAt(i)) { + return false; + } } - } - return true; - } /* - else if (o instanceof UnicodeValue) { - UnicodeValue value = (UnicodeValue)o; + return true; + } else if (o instanceof UnicodeValue) { + UnicodeValue value = (UnicodeValue) o; - return value.equals(this); - } - */ else { - return false; - } - } + int length = _length; + int lengthB = value.length(); - @Override - public boolean eql(Value o) { - o = o.toValue(); + if (length != lengthB) { + return false; + } + + byte[] bufferA = _buffer; - if (o == this) { - return true; - } + for (int i = length - 1; i >= 0; i--) { + if (bufferA[i] != value.charAt(i)) { + return false; + } + } + + return true; + } /* + else if (o instanceof UnicodeValue) { + UnicodeValue value = (UnicodeValue)o; + + return value.equals(this); + } + */ else { + return false; + } + } - if (o instanceof StringBuilderValue) { - StringBuilderValue value = (StringBuilderValue) o; + @Override + public boolean eql(Value o) { + o = o.toValue(); - int length = _length; + if (o == this) { + return true; + } - if (length != value._length) { + if (o instanceof StringBuilderValue) { + StringBuilderValue value = (StringBuilderValue) o; + + int length = _length; + + if (length != value._length) { + return false; + } + + byte[] bufferA = _buffer; + byte[] bufferB = value._buffer; + + for (int i = length - 1; i >= 0; i--) { + if (bufferA[i] != bufferB[i]) { + return false; + } + } + + return true; + } else { return false; - } + } + } + + // + // Java serialization code + // + private void writeObject(ObjectOutputStream out) + throws IOException { + out.writeInt(_length); + + out.write(_buffer, 0, _length); + } + + private void readObject(ObjectInputStream in) + throws ClassNotFoundException, IOException { + _length = in.readInt(); + _buffer = new byte[_length]; + + in.read(_buffer, 0, _length); + } + + class BinaryInputStream extends InputStream { + + private int _offset; + + /** + * Reads the next byte. + */ + @Override + public int read() { + if (_offset < _length) { + return _buffer[_offset++] & 0xFF; + } else { + return -1; + } + } - byte[] bufferA = _buffer; - byte[] bufferB = value._buffer; + /** + * Reads into a buffer. + */ + @Override + public int read(byte[] buffer, int offset, int length) { + int sublen = Math.min(_length - _offset, length); - for (int i = length - 1; i >= 0; i--) { - if (bufferA[i] != bufferB[i]) { - return false; + if (sublen <= 0) { + return -1; } - } - - return true; - } else { - return false; - } - } - - // - // Java serialization code - // - private void writeObject(ObjectOutputStream out) - throws IOException { - out.writeInt(_length); - - out.write(_buffer, 0, _length); - } - - private void readObject(ObjectInputStream in) - throws ClassNotFoundException, IOException { - _length = in.readInt(); - _buffer = new byte[_length]; - - in.read(_buffer, 0, _length); - } - - class BinaryInputStream extends InputStream { - - private int _offset; - - /** - * Reads the next byte. - */ - @Override - public int read() { - if (_offset < _length) { - return _buffer[_offset++] & 0xFF; - } else { - return -1; - } - } - /** - * Reads into a buffer. - */ - @Override - public int read(byte[] buffer, int offset, int length) { - int sublen = Math.min(_length - _offset, length); + System.arraycopy(_buffer, _offset, buffer, offset, sublen); - if (sublen <= 0) { - return -1; - } + _offset += sublen; - System.arraycopy(_buffer, _offset, buffer, offset, sublen); + return sublen; + } + } - _offset += sublen; + class BuilderInputStream extends InputStream { - return sublen; - } - } + private int _index; - class BuilderInputStream extends InputStream { + /** + * Reads the next byte. + */ + @Override + public int read() { + if (_index < _length) { + return _buffer[_index++] & 0xFF; + } else { + return -1; + } + } - private int _index; + /** + * Reads into a buffer. + */ + @Override + public int read(byte[] buffer, int offset, int length) { + int sublen = Math.min(_length - _index, length); - /** - * Reads the next byte. - */ - @Override - public int read() { - if (_index < _length) { - return _buffer[_index++] & 0xFF; - } else { - return -1; - } - } + if (sublen <= 0) { + return -1; + } - /** - * Reads into a buffer. - */ - @Override - public int read(byte[] buffer, int offset, int length) { - int sublen = Math.min(_length - _index, length); + System.arraycopy(_buffer, _index, buffer, offset, sublen); - if (sublen <= 0) { - return -1; - } - - System.arraycopy(_buffer, _index, buffer, offset, sublen); - - _index += sublen; - - return sublen; - } - } - - class BuilderOutputStream extends OutputStream { - - /** - * Writes the next byte. - */ - @Override - public void write(int ch) { - appendByte(ch); - } - - /** - * Reads into a buffer. - */ - @Override - public void write(byte[] buffer, int offset, int length) { - append(buffer, offset, length); - } - } - - static { - CHAR_STRINGS = new ConstStringValue[256]; - - for (int i = 0; i < CHAR_STRINGS.length; i++) { - CHAR_STRINGS[i] = new ConstStringValue((char) i); - } - } + _index += sublen; + + return sublen; + } + } + + class BuilderOutputStream extends OutputStream { + + /** + * Writes the next byte. + */ + @Override + public void write(int ch) { + appendByte(ch); + } + + /** + * Reads into a buffer. + */ + @Override + public void write(byte[] buffer, int offset, int length) { + append(buffer, offset, length); + } + } + + static { + CHAR_STRINGS = new ConstStringValue[256]; + + for (int i = 0; i < CHAR_STRINGS.length; i++) { + CHAR_STRINGS[i] = new ConstStringValue((char) i); + } + } } diff --git a/quercus/src/main/java/com/caucho/quercus/env/StringValue.java b/quercus/src/main/java/com/caucho/quercus/env/StringValue.java index 5a55e75..41db127 100644 --- a/quercus/src/main/java/com/caucho/quercus/env/StringValue.java +++ b/quercus/src/main/java/com/caucho/quercus/env/StringValue.java @@ -56,2515 +56,2517 @@ abstract public class StringValue extends Value implements CharSequence, ByteAppendable { - public static final StringValue EMPTY = new ConstStringValue(""); - protected static final int MIN_LENGTH = 32; - protected static final int IS_STRING = 0; - protected static final int IS_LONG = 1; - protected static final int IS_DOUBLE = 2; - - /** - * Creates a string builder of the same type. - */ - abstract public StringValue createStringBuilder(); - - /** - * Creates a string builder of the same type. - */ - abstract public StringValue createStringBuilder(int length); - - /** - * Creates the string. - */ - public static Value create(String value) { - // TODO: needs updating for i18n, currently php5 only - - if (value == null) { - return NullValue.NULL; - } else { - return new ConstStringValue(value); - } - } - - /** - * Creates the string. - */ - public static StringValue create(char value) { - // TODO: needs updating for i18n, currently php5 only - - return ConstStringValue.create(value); - - /* - if (value < CHAR_STRINGS.length) - return CHAR_STRINGS[value]; - else - return new StringBuilderValue(String.valueOf(value)); - */ - } - - /** - * Creates the string. - */ - public static Value create(Object value) { - // TODO: needs updating for i18n, currently php5 only - - if (value == null) { - return NullValue.NULL; - } else { - return new StringBuilderValue(value.toString()); - } - } - - /* - * Decodes the Unicode str from charset. - * - * @param str should be a Unicode string - * @param charset to decode string from - */ - public StringValue create(Env env, StringValue unicodeStr, String charset) { - if (!unicodeStr.isUnicode()) { - return unicodeStr; - } - - try { - StringValue sb = createStringBuilder(); - - byte[] bytes = unicodeStr.toString().getBytes(charset); - - sb.append(bytes); - return sb; - - } catch (UnsupportedEncodingException e) { - env.warning(e); - - return unicodeStr; - } - } - - // - // Predicates and relations - // - /** - * Returns the type. - */ - @Override - public String getType() { - return "string"; - } - - /** - * Returns the ValueType. - */ - @Override - public ValueType getValueType() { - return ValueType.STRING; - } - - /** - * Returns true for a long - */ - @Override - public boolean isLongConvertible() { - return getValueType().isLongCmp(); - } - - /** - * Returns true for a double - */ - @Override - public boolean isDoubleConvertible() { - return getValueType().isNumberCmp(); - } - - /** - * Returns true for a number - */ - public boolean isNumber() { - return false; - } - - /** - * Returns true for is_numeric - */ - @Override - public boolean isNumeric() { - // php/120y - - return getValueType().isNumberCmp(); - } - - /** - * Returns true for a scalar - */ - public boolean isScalar() { - return true; - } - - /** - * Returns true for StringValue - */ - @Override - public boolean isString() { - return true; - } - - /* - * Returns true if this is a PHP5 string. - */ - public boolean isPHP5String() { - return false; - } - - /** - * Returns true if the value is empty - */ - @Override - public boolean isEmpty() { - return length() == 0 || length() == 1 && charAt(0) == '0'; - } - - // - // marshal cost - // - /** - * Cost to convert to a double - */ - @Override - public int toDoubleMarshalCost() { - ValueType valueType = getValueType(); - - if (valueType.isLongCmp()) { - return Marshal.COST_TO_CHAR_ARRAY + 20; - } else if (valueType.isNumberCmp()) { - return Marshal.COST_TO_CHAR_ARRAY + 10; - } else { - return Marshal.COST_INCOMPATIBLE; - } - } - - /** - * Cost to convert to a float - */ - @Override - public int toFloatMarshalCost() { - ValueType valueType = getValueType(); - - if (valueType.isLongCmp()) { - return Marshal.COST_TO_CHAR_ARRAY + 25; - } else if (valueType.isNumberCmp()) { - return Marshal.COST_TO_CHAR_ARRAY + 15; - } else { - return Marshal.COST_INCOMPATIBLE; - } - } - - /** - * Cost to convert to a long - */ - @Override - public int toLongMarshalCost() { - ValueType valueType = getValueType(); - - if (valueType.isLongCmp()) { - return Marshal.COST_TO_CHAR_ARRAY + 10; - } else if (valueType.isNumberCmp()) { - return Marshal.COST_TO_CHAR_ARRAY + 40; - } else { - return Marshal.COST_INCOMPATIBLE; - } - } - - /** - * Cost to convert to an integer - */ - @Override - public int toIntegerMarshalCost() { - ValueType valueType = getValueType(); - - if (valueType.isLongCmp()) { - return Marshal.COST_TO_CHAR_ARRAY + 10; - } else if (valueType.isNumberCmp()) { - return Marshal.COST_TO_CHAR_ARRAY + 40; - } else { - return Marshal.COST_INCOMPATIBLE; - } - } - - /** - * Cost to convert to a short - */ - @Override - public int toShortMarshalCost() { - ValueType valueType = getValueType(); - - if (valueType.isLongCmp()) { - return Marshal.COST_TO_CHAR_ARRAY + 30; - } else if (valueType.isNumberCmp()) { - return Marshal.COST_TO_CHAR_ARRAY + 50; - } else { - return Marshal.COST_INCOMPATIBLE; - } - } - - /** - * Cost to convert to a byte - */ - @Override - public int toByteMarshalCost() { - ValueType valueType = getValueType(); - - if (valueType.isLongCmp()) { - return Marshal.COST_TO_CHAR_ARRAY + 30; - } else if (valueType.isNumberCmp()) { - return Marshal.COST_TO_CHAR_ARRAY + 50; - } else { - return Marshal.COST_STRING_TO_BYTE; - } - } - - /** - * Cost to convert to a character - */ - @Override - public int toCharMarshalCost() { - return Marshal.COST_STRING_TO_CHAR; - } - - /** - * Cost to convert to a String - */ - @Override - public int toStringMarshalCost() { - return Marshal.COST_EQUAL; - } - - /** - * Cost to convert to a char[] - */ - @Override - public int toCharArrayMarshalCost() { - return Marshal.COST_STRING_TO_CHAR_ARRAY; - } - - /** - * Cost to convert to a StringValue - */ - @Override - public int toStringValueMarshalCost() { - return Marshal.COST_IDENTICAL; - } - - /** - * Cost to convert to a binary value - */ - @Override - public int toBinaryValueMarshalCost() { - return Marshal.COST_STRING_TO_BINARY; - } - - /** - * Returns true for equality - */ - @Override - public int cmp(Value rValue) { - if (isNumberConvertible() || rValue.isNumberConvertible()) { - double l = toDouble(); - double r = rValue.toDouble(); - - if (l == r) { - return 0; - } else if (l < r) { - return -1; - } else { - return 1; - } - } else { - int result = toString().compareTo(rValue.toString()); + public static final StringValue EMPTY = new ConstStringValue(""); + protected static final int MIN_LENGTH = 32; + protected static final int IS_STRING = 0; + protected static final int IS_LONG = 1; + protected static final int IS_DOUBLE = 2; + + /** + * Creates a string builder of the same type. + */ + abstract public StringValue createStringBuilder(); + + /** + * Creates a string builder of the same type. + */ + abstract public StringValue createStringBuilder(int length); + + /** + * Creates the string. + */ + public static Value create(String value) { + // TODO: needs updating for i18n, currently php5 only + + if (value == null) { + return NullValue.NULL; + } else { + return new ConstStringValue(value); + } + } + + /** + * Creates the string. + */ + public static StringValue create(char value) { + // TODO: needs updating for i18n, currently php5 only + + return ConstStringValue.create(value); + + /* + if (value < CHAR_STRINGS.length) + return CHAR_STRINGS[value]; + else + return new StringBuilderValue(String.valueOf(value)); + */ + } + + /** + * Creates the string. + */ + public static Value create(Object value) { + // TODO: needs updating for i18n, currently php5 only + + if (value == null) { + return NullValue.NULL; + } else { + return new StringBuilderValue(value.toString()); + } + } + + /* + * Decodes the Unicode str from charset. + * + * @param str should be a Unicode string + * @param charset to decode string from + */ + public StringValue create(Env env, StringValue unicodeStr, String charset) { + if (!unicodeStr.isUnicode()) { + return unicodeStr; + } + + try { + StringValue sb = createStringBuilder(); + + byte[] bytes = unicodeStr.toString().getBytes(charset); + + sb.append(bytes); + return sb; + + } catch (UnsupportedEncodingException e) { + env.warning(e); + + return unicodeStr; + } + } + + // + // Predicates and relations + // + /** + * Returns the type. + */ + @Override + public String getType() { + return "string"; + } + + /** + * Returns the ValueType. + */ + @Override + public ValueType getValueType() { + return ValueType.STRING; + } + + /** + * Returns true for a long + */ + @Override + public boolean isLongConvertible() { + return getValueType().isLongCmp(); + } + + /** + * Returns true for a double + */ + @Override + public boolean isDoubleConvertible() { + return getValueType().isNumberCmp(); + } + + /** + * Returns true for a number + */ + public boolean isNumber() { + return false; + } + + /** + * Returns true for is_numeric + */ + @Override + public boolean isNumeric() { + // php/120y + + return getValueType().isNumberCmp(); + } + + /** + * Returns true for a scalar + */ + public boolean isScalar() { + return true; + } + + /** + * Returns true for StringValue + */ + @Override + public boolean isString() { + return true; + } + + /* + * Returns true if this is a PHP5 string. + */ + public boolean isPHP5String() { + return false; + } + + /** + * Returns true if the value is empty + */ + @Override + public boolean isEmpty() { + return length() == 0 || length() == 1 && charAt(0) == '0'; + } + + // + // marshal cost + // + /** + * Cost to convert to a double + */ + @Override + public int toDoubleMarshalCost() { + ValueType valueType = getValueType(); + + if (valueType.isLongCmp()) { + return Marshal.COST_TO_CHAR_ARRAY + 20; + } else if (valueType.isNumberCmp()) { + return Marshal.COST_TO_CHAR_ARRAY + 10; + } else { + return Marshal.COST_INCOMPATIBLE; + } + } + + /** + * Cost to convert to a float + */ + @Override + public int toFloatMarshalCost() { + ValueType valueType = getValueType(); + + if (valueType.isLongCmp()) { + return Marshal.COST_TO_CHAR_ARRAY + 25; + } else if (valueType.isNumberCmp()) { + return Marshal.COST_TO_CHAR_ARRAY + 15; + } else { + return Marshal.COST_INCOMPATIBLE; + } + } + + /** + * Cost to convert to a long + */ + @Override + public int toLongMarshalCost() { + ValueType valueType = getValueType(); + + if (valueType.isLongCmp()) { + return Marshal.COST_TO_CHAR_ARRAY + 10; + } else if (valueType.isNumberCmp()) { + return Marshal.COST_TO_CHAR_ARRAY + 40; + } else { + return Marshal.COST_INCOMPATIBLE; + } + } + + /** + * Cost to convert to an integer + */ + @Override + public int toIntegerMarshalCost() { + ValueType valueType = getValueType(); + + if (valueType.isLongCmp()) { + return Marshal.COST_TO_CHAR_ARRAY + 10; + } else if (valueType.isNumberCmp()) { + return Marshal.COST_TO_CHAR_ARRAY + 40; + } else { + return Marshal.COST_INCOMPATIBLE; + } + } + + /** + * Cost to convert to a short + */ + @Override + public int toShortMarshalCost() { + ValueType valueType = getValueType(); + + if (valueType.isLongCmp()) { + return Marshal.COST_TO_CHAR_ARRAY + 30; + } else if (valueType.isNumberCmp()) { + return Marshal.COST_TO_CHAR_ARRAY + 50; + } else { + return Marshal.COST_INCOMPATIBLE; + } + } + + /** + * Cost to convert to a byte + */ + @Override + public int toByteMarshalCost() { + ValueType valueType = getValueType(); + + if (valueType.isLongCmp()) { + return Marshal.COST_TO_CHAR_ARRAY + 30; + } else if (valueType.isNumberCmp()) { + return Marshal.COST_TO_CHAR_ARRAY + 50; + } else { + return Marshal.COST_STRING_TO_BYTE; + } + } + + /** + * Cost to convert to a character + */ + @Override + public int toCharMarshalCost() { + return Marshal.COST_STRING_TO_CHAR; + } + + /** + * Cost to convert to a String + */ + @Override + public int toStringMarshalCost() { + return Marshal.COST_EQUAL; + } + + /** + * Cost to convert to a char[] + */ + @Override + public int toCharArrayMarshalCost() { + return Marshal.COST_STRING_TO_CHAR_ARRAY; + } + + /** + * Cost to convert to a StringValue + */ + @Override + public int toStringValueMarshalCost() { + return Marshal.COST_IDENTICAL; + } + + /** + * Cost to convert to a binary value + */ + @Override + public int toBinaryValueMarshalCost() { + return Marshal.COST_STRING_TO_BINARY; + } + + /** + * Returns true for equality + */ + @Override + public int cmp(Value rValue) { + if (isNumberConvertible() || rValue.isNumberConvertible()) { + double l = toDouble(); + double r = rValue.toDouble(); + + if (l == r) { + return 0; + } else if (l < r) { + return -1; + } else { + return 1; + } + } else { + int result = toString().compareTo(rValue.toString()); - if (result == 0) { + if (result == 0) { + return 0; + } else if (result > 0) { + return 1; + } else { + return -1; + } + } + } + + /** + * Returns true for equality + */ + @Override + public boolean eq(Value rValue) { + //System.err.println("eq " + rValue + " to this " + this); + ValueType typeA = getValueType(); + ValueType typeB = rValue.getValueType(); + + if (typeB.isNumber()) { + double l = toDouble(); + double r = rValue.toDouble(); + + return l == r; + } else if (typeB.isBoolean()) { + return toBoolean() == rValue.toBoolean(); + } else if (typeA.isNumberCmp() && typeB.isNumberCmp()) { + double l = toDouble(); + double r = rValue.toDouble(); + + return l == r; + } else { + return toString().equals(rValue.toString()); + } + } + + /** + * Compare two strings + */ + public int cmpString(StringValue rValue) { + return toString().compareTo(rValue.toString()); + } + + // Conversions + /** + * Converts to a string value. + */ + @Override + public StringValue toStringValue() { + return this; + } + + /** + * Converts to a string value. + */ + @Override + public StringValue toStringValue(Env env) { + return this; + } + + /** + * Converts to a long. + */ + public static long toLong(String string) { + return parseLong(string); + } + + /** + * String to long conversion routines used by this module + * and other modules in this package. These methods are + * only invoked by other implementations of a "string" object. + * The 3 implementations should be identical except for the + * char data source. + */ + static long parseLong(char[] buffer, int offset, int len) { + if (len == 0) { return 0; - } else if (result > 0) { - return 1; - } else { - return -1; - } - } - } - - /** - * Returns true for equality - */ - @Override - public boolean eq(Value rValue) { - ValueType typeA = getValueType(); - ValueType typeB = rValue.getValueType(); - - if (typeB.isNumber()) { - double l = toDouble(); - double r = rValue.toDouble(); - - return l == r; - } else if (typeB.isBoolean()) { - return toBoolean() == rValue.toBoolean(); - } else if (typeA.isNumberCmp() && typeB.isNumberCmp()) { - double l = toDouble(); - double r = rValue.toDouble(); - - return l == r; - } else { - return toString().equals(rValue.toString()); - } - } - - /** - * Compare two strings - */ - public int cmpString(StringValue rValue) { - return toString().compareTo(rValue.toString()); - } - - // Conversions - /** - * Converts to a string value. - */ - @Override - public StringValue toStringValue() { - return this; - } - - /** - * Converts to a string value. - */ - @Override - public StringValue toStringValue(Env env) { - return this; - } - - /** - * Converts to a long. - */ - public static long toLong(String string) { - return parseLong(string); - } - - /** - * String to long conversion routines used by this module - * and other modules in this package. These methods are - * only invoked by other implementations of a "string" object. - * The 3 implementations should be identical except for the - * char data source. - */ - static long parseLong(char[] buffer, int offset, int len) { - if (len == 0) { - return 0; - } - - long value = 0; - long sign = 1; - boolean isResultSet = false; - long result = 0; - - int end = offset + len; - - while (offset < end && Character.isWhitespace(buffer[offset])) { - offset++; - } - - int ch; - - if (offset + 1 < end && buffer[offset] == '0' - && ((ch = buffer[offset + 1]) == 'x' || ch == 'X')) { - - for (offset += 2; offset < end; offset++) { - ch = buffer[offset] & 0xFF; - - long oldValue = value; + } + + long value = 0; + long sign = 1; + boolean isResultSet = false; + long result = 0; + + int end = offset + len; + + while (offset < end && Character.isWhitespace(buffer[offset])) { + offset++; + } + + int ch; + + if (offset + 1 < end && buffer[offset] == '0' + && ((ch = buffer[offset + 1]) == 'x' || ch == 'X')) { + + for (offset += 2; offset < end; offset++) { + ch = buffer[offset] & 0xFF; + + long oldValue = value; + + if ('0' <= ch && ch <= '9') { + value = value * 16 + ch - '0'; + } else if ('a' <= ch && ch <= 'z') { + value = value * 16 + ch - 'a' + 10; + } else if ('A' <= ch && ch <= 'Z') { + value = value * 16 + ch - 'A' + 10; + } else { + return value; + } + + if (value < oldValue) { + return Integer.MAX_VALUE; + } + } + + return value; + } + + if (offset < end && buffer[offset] == '-') { + sign = -1; + offset++; + } else if (offset < end && buffer[offset] == '+') { + sign = +1; + offset++; + } + + while (offset < end) { + ch = buffer[offset++]; if ('0' <= ch && ch <= '9') { - value = value * 16 + ch - '0'; - } else if ('a' <= ch && ch <= 'z') { - value = value * 16 + ch - 'a' + 10; - } else if ('A' <= ch && ch <= 'Z') { - value = value * 16 + ch - 'A' + 10; + long newValue = 10 * value + ch - '0'; + if (newValue < value) { + // php/0143 + // long value overflowed + result = Integer.MAX_VALUE; + isResultSet = true; + break; + } + value = newValue; } else { - return value; + result = sign * value; + isResultSet = true; + break; } + } - if (value < oldValue) { - return Integer.MAX_VALUE; - } - } - - return value; - } - - if (offset < end && buffer[offset] == '-') { - sign = -1; - offset++; - } else if (offset < end && buffer[offset] == '+') { - sign = +1; - offset++; - } - - while (offset < end) { - ch = buffer[offset++]; - - if ('0' <= ch && ch <= '9') { - long newValue = 10 * value + ch - '0'; - if (newValue < value) { - // php/0143 - // long value overflowed - result = Integer.MAX_VALUE; - isResultSet = true; - break; - } - value = newValue; - } else { + if (!isResultSet) { result = sign * value; - isResultSet = true; - break; - } - } + } - if (!isResultSet) { - result = sign * value; - } + return result; + } - return result; - } + static long parseLong(byte[] buffer, int offset, int len) { + if (len == 0) { + return 0; + } + + long value = 0; + long sign = 1; + boolean isResultSet = false; + long result = 0; + + int end = offset + len; - static long parseLong(byte[] buffer, int offset, int len) { - if (len == 0) { - return 0; - } + while (offset < end && Character.isWhitespace(buffer[offset])) { + offset++; + } - long value = 0; - long sign = 1; - boolean isResultSet = false; - long result = 0; + int ch; - int end = offset + len; + if (offset + 1 < end && buffer[offset] == '0' + && ((ch = buffer[offset + 1]) == 'x' || ch == 'X')) { - while (offset < end && Character.isWhitespace(buffer[offset])) { - offset++; - } + for (offset += 2; offset < end; offset++) { + ch = buffer[offset] & 0xFF; - int ch; + long oldValue = value; + + if ('0' <= ch && ch <= '9') { + value = value * 16 + ch - '0'; + } else if ('a' <= ch && ch <= 'z') { + value = value * 16 + ch - 'a' + 10; + } else if ('A' <= ch && ch <= 'Z') { + value = value * 16 + ch - 'A' + 10; + } else { + return value; + } + + if (value < oldValue) { + return Integer.MAX_VALUE; + } + } - if (offset + 1 < end && buffer[offset] == '0' - && ((ch = buffer[offset + 1]) == 'x' || ch == 'X')) { + return value; + } - for (offset += 2; offset < end; offset++) { - ch = buffer[offset] & 0xFF; + if (offset < end && buffer[offset] == '-') { + sign = -1; + offset++; + } else if (offset < end && buffer[offset] == '+') { + sign = +1; + offset++; + } - long oldValue = value; + while (offset < end) { + ch = buffer[offset++]; if ('0' <= ch && ch <= '9') { - value = value * 16 + ch - '0'; - } else if ('a' <= ch && ch <= 'z') { - value = value * 16 + ch - 'a' + 10; - } else if ('A' <= ch && ch <= 'Z') { - value = value * 16 + ch - 'A' + 10; + long newValue = 10 * value + ch - '0'; + if (newValue < value) { + // long value overflowed, set result to integer max + result = Integer.MAX_VALUE; + isResultSet = true; + break; + } + value = newValue; } else { - return value; + result = sign * value; + isResultSet = true; + break; } + } - if (value < oldValue) { - return Integer.MAX_VALUE; - } - } - - return value; - } - - if (offset < end && buffer[offset] == '-') { - sign = -1; - offset++; - } else if (offset < end && buffer[offset] == '+') { - sign = +1; - offset++; - } - - while (offset < end) { - ch = buffer[offset++]; - - if ('0' <= ch && ch <= '9') { - long newValue = 10 * value + ch - '0'; - if (newValue < value) { - // long value overflowed, set result to integer max - result = Integer.MAX_VALUE; - isResultSet = true; - break; - } - value = newValue; - } else { + if (!isResultSet) { result = sign * value; - isResultSet = true; - break; - } - } - - if (!isResultSet) { - result = sign * value; - } - - return result; - } - - static long parseLong(CharSequence string) { - final int len = string.length(); - - if (len == 0) { - return 0; - } - - long value = 0; - long sign = 1; - boolean isResultSet = false; - long result = 0; - - int offset = 0; - int end = offset + len; - - while (offset < end && Character.isWhitespace(string.charAt(offset))) { - offset++; - } - - if (offset < end && string.charAt(offset) == '-') { - sign = -1; - offset++; - } else if (offset < end && string.charAt(offset) == '+') { - sign = +1; - offset++; - } - - while (offset < end) { - int ch = string.charAt(offset++); - - if ('0' <= ch && ch <= '9') { - long newValue = 10 * value + ch - '0'; - if (newValue < value) { - // long value overflowed, set result to integer max - result = Integer.MAX_VALUE; - isResultSet = true; - break; + } + + return result; + } + + static long parseLong(CharSequence string) { + final int len = string.length(); + + if (len == 0) { + return 0; + } + + long value = 0; + long sign = 1; + boolean isResultSet = false; + long result = 0; + + int offset = 0; + int end = offset + len; + + while (offset < end && Character.isWhitespace(string.charAt(offset))) { + offset++; + } + + if (offset < end && string.charAt(offset) == '-') { + sign = -1; + offset++; + } else if (offset < end && string.charAt(offset) == '+') { + sign = +1; + offset++; + } + + while (offset < end) { + int ch = string.charAt(offset++); + + if ('0' <= ch && ch <= '9') { + long newValue = 10 * value + ch - '0'; + if (newValue < value) { + // long value overflowed, set result to integer max + result = Integer.MAX_VALUE; + isResultSet = true; + break; + } + value = newValue; + } else { + result = sign * value; + isResultSet = true; + break; } - value = newValue; - } else { + } + + if (!isResultSet) { result = sign * value; - isResultSet = true; - break; - } - } + } - if (!isResultSet) { - result = sign * value; - } + return result; + } - return result; - } + /** + * Converts to a double. + */ + @Override + public double toDouble() { + return toDouble(toString()); + } - /** - * Converts to a double. - */ - @Override - public double toDouble() { - return toDouble(toString()); - } + /** + * Converts to a double. + */ + public static double toDouble(String s) { + int len = s.length(); - /** - * Converts to a double. - */ - public static double toDouble(String s) { - int len = s.length(); + int start = 0; - int start = 0; + int i = 0; + int ch = 0; - int i = 0; - int ch = 0; + while (i < len && Character.isWhitespace(s.charAt(i))) { + start++; + i++; + } - while (i < len && Character.isWhitespace(s.charAt(i))) { - start++; - i++; - } + if (i + 1 < len && s.charAt(i) == '0' + && ((ch = s.charAt(i)) == 'x' || ch == 'X')) { - if (i + 1 < len && s.charAt(i) == '0' - && ((ch = s.charAt(i)) == 'x' || ch == 'X')) { + double value = 0; - double value = 0; + for (i += 2; i < len; i++) { + ch = s.charAt(i); - for (i += 2; i < len; i++) { - ch = s.charAt(i); + if ('0' <= ch && ch <= '9') { + value = value * 16 + ch - '0'; + } else if ('a' <= ch && ch <= 'z') { + value = value * 16 + ch - 'a' + 10; + } else if ('A' <= ch && ch <= 'Z') { + value = value * 16 + ch - 'A' + 10; + } else { + return value; + } + } - if ('0' <= ch && ch <= '9') { - value = value * 16 + ch - '0'; - } else if ('a' <= ch && ch <= 'z') { - value = value * 16 + ch - 'a' + 10; - } else if ('A' <= ch && ch <= 'Z') { - value = value * 16 + ch - 'A' + 10; - } else { - return value; + return value; + } + + if (i < len && ((ch = s.charAt(i)) == '+' || ch == '-')) { + i++; + } + + for (; i < len && '0' <= (ch = s.charAt(i)) && ch <= '9'; i++) { + } + + if (ch == '.') { + for (i++; i < len && '0' <= (ch = s.charAt(i)) && ch <= '9'; i++) { } - } + } - return value; - } + if (ch == 'e' || ch == 'E') { + int e = i++; - if (i < len && ((ch = s.charAt(i)) == '+' || ch == '-')) { - i++; - } + if (i < len && (ch = s.charAt(i)) == '+' || ch == '-') { + i++; + } - for (; i < len && '0' <= (ch = s.charAt(i)) && ch <= '9'; i++) { - } + for (; i < len && '0' <= (ch = s.charAt(i)) && ch <= '9'; i++) { + } + + if (i == e + 1) { + i = e; + } + } - if (ch == '.') { - for (i++; i < len && '0' <= (ch = s.charAt(i)) && ch <= '9'; i++) { - } - } + if (i == 0) { + return 0; + } else if (i == len && start == 0) { + return Double.parseDouble(s); + } else { + return Double.parseDouble(s.substring(start, i)); + } + } + + /** + * Converts to a boolean. + */ + @Override + public boolean toBoolean() { + int length = length(); + + if (length == 0) { + return false; + } else if (length > 1) { + return true; + } else { + return charAt(0) != '0'; + } + } + + /** + * Converts to a key. + */ + @Override + public Value toKey() { + int len = length(); + + if (len == 0) { + return this; + } - if (ch == 'e' || ch == 'E') { - int e = i++; + int sign = 1; + long value = 0; - if (i < len && (ch = s.charAt(i)) == '+' || ch == '-') { + int i = 0; + char ch = charAt(i); + if (ch == '-') { + sign = -1; i++; - } - - for (; i < len && '0' <= (ch = s.charAt(i)) && ch <= '9'; i++) { - } - - if (i == e + 1) { - i = e; - } - } - - if (i == 0) { - return 0; - } else if (i == len && start == 0) { - return Double.parseDouble(s); - } else { - return Double.parseDouble(s.substring(start, i)); - } - } - - /** - * Converts to a boolean. - */ - @Override - public boolean toBoolean() { - int length = length(); - - if (length == 0) { - return false; - } else if (length > 1) { - return true; - } else { - return charAt(0) != '0'; - } - } - - /** - * Converts to a key. - */ - @Override - public Value toKey() { - int len = length(); - - if (len == 0) { - return this; - } - - int sign = 1; - long value = 0; - - int i = 0; - char ch = charAt(i); - if (ch == '-') { - sign = -1; - i++; - } - - for (; i < len; i++) { - ch = charAt(i); - - if ('0' <= ch && ch <= '9') { - value = 10 * value + ch - '0'; - } else { + } + + for (; i < len; i++) { + ch = charAt(i); + + if ('0' <= ch && ch <= '9') { + value = 10 * value + ch - '0'; + } else { + return this; + } + } + + return LongValue.create(sign * value); + } + + /** + * Converts to an object. + */ + @Override + final public Value toAutoObject(Env env) { + return env.createObject(); + } + + /** + * Converts to an array if null. + */ + @Override + public Value toAutoArray() { + if (length() == 0) { + return new ArrayValueImpl(); + } else { return this; - } - } - - return LongValue.create(sign * value); - } - - /** - * Converts to an object. - */ - @Override - final public Value toAutoObject(Env env) { - return env.createObject(); - } - - /** - * Converts to an array if null. - */ - @Override - public Value toAutoArray() { - if (length() == 0) { - return new ArrayValueImpl(); - } else { - return this; - } - } - - /** - * Converts to a Java object. - */ - @Override - public Object toJavaObject() { - return toString(); - } - - /** - * Takes the values of this array, unmarshalls them to objects of type - * elementType, and puts them in a java array. - */ - @Override - public Object valuesToArray(Env env, Class elementType) { - if (char.class.equals(elementType)) { - return toUnicode(env).toCharArray(); - } else if (Character.class.equals(elementType)) { - char[] chars = toUnicode(env).toCharArray(); - - int length = chars.length; - - Character[] charObjects = new Character[length]; - - for (int i = 0; i < length; i++) { - charObjects[i] = Character.valueOf(chars[i]); - } - - return charObjects; - } else if (byte.class.equals(elementType)) { - return toBinaryValue(env).toBytes(); - } else if (Byte.class.equals(elementType)) { - byte[] bytes = toBinaryValue(env).toBytes(); - - int length = bytes.length; - - Byte[] byteObjects = new Byte[length]; - - for (int i = 0; i < length; i++) { - byteObjects[i] = Byte.valueOf(bytes[i]); - } - - return byteObjects; - } else { - env.error(L.l("Can't assign {0} with type {1} to {2}", - this, - this.getClass(), - elementType)); - return null; - } - } - - /** - * Converts to a callable object - */ - @Override - public Callable toCallable(Env env) { - // php/1h0o - if (isEmpty()) { - return super.toCallable(env); - } - - String s = toString(); - - int p = s.indexOf("::"); - - if (p < 0) { - return new CallbackFunction(env, s); - } else { - String className = s.substring(0, p); - String methodName = s.substring(p + 2); - - QuercusClass cl = env.findClass(className); - - if (cl == null) { - env.warning(L.l("can't find class {0}", - className)); + } + } + + /** + * Converts to a Java object. + */ + @Override + public Object toJavaObject() { + return toString(); + } + + /** + * Takes the values of this array, unmarshalls them to objects of type + * elementType, and puts them in a java array. + */ + @Override + public Object valuesToArray(Env env, Class elementType) { + if (char.class.equals(elementType)) { + return toUnicode(env).toCharArray(); + } else if (Character.class.equals(elementType)) { + char[] chars = toUnicode(env).toCharArray(); + + int length = chars.length; + + Character[] charObjects = new Character[length]; + + for (int i = 0; i < length; i++) { + charObjects[i] = Character.valueOf(chars[i]); + } + + return charObjects; + } else if (byte.class.equals(elementType)) { + return toBinaryValue(env).toBytes(); + } else if (Byte.class.equals(elementType)) { + byte[] bytes = toBinaryValue(env).toBytes(); + + int length = bytes.length; + Byte[] byteObjects = new Byte[length]; + + for (int i = 0; i < length; i++) { + byteObjects[i] = Byte.valueOf(bytes[i]); + } + + return byteObjects; + } else { + env.error(L.l("Can't assign {0} with type {1} to {2}", + this, + this.getClass(), + elementType)); + return null; + } + } + + /** + * Converts to a callable object + */ + @Override + public Callable toCallable(Env env) { + // php/1h0o + if (isEmpty()) { return super.toCallable(env); - } - - return new CallbackClassMethod(cl, env.createString(methodName)); - } - } - - /** - * Sets the array value, returning the new array, e.g. to handle - * string update ($a[0] = 'A'). Creates an array automatically if - * necessary. - */ - @Override - public Value append(Value index, Value value) { - if (length() == 0) { - return new ArrayValueImpl().append(index, value); - } else { - return this; - } - } - - // Operations - /** - * Returns the character at an index - */ - @Override - public Value get(Value key) { - return charValueAt(key.toLong()); - } - - /** - * Returns the character at an index - */ - @Override - public Value getArg(Value key, boolean isTop) { - // php/03ma - return charValueAt(key.toLong()); - } - - /** - * Returns the character at an index - */ - @Override - public Value charValueAt(long index) { - int len = length(); - - if (index < 0 || len <= index) { - return UnsetUnicodeValue.UNSET; - } else { - return StringValue.create(charAt((int) index)); - } - } - - /** - * sets the character at an index - */ - @Override - public Value setCharValueAt(long index, Value value) { - //XXX: need to double-check this for non-string values - - int len = length(); - - if (index < 0 || len <= index) { - return this; - } else { - return (createStringBuilder().append(this, 0, (int) index).append(value).append(this, (int) (index + 1), length())); - } - } - - /** - * Increment the following value. - */ - @Override - public Value increment(int incr) { - // php/03i6 - if (length() == 0) { - if (incr == 1) { - return createStringBuilder().append("1"); - } else { - return LongValue.MINUS_ONE; - } - } - - if (incr > 0) { - StringBuilder tail = new StringBuilder(); - - for (int i = length() - 1; i >= 0; i--) { - char ch = charAt(i); - - if (ch == 'z') { - if (i == 0) { - return createStringBuilder().append("aa").append(tail); - } else { - tail.insert(0, 'a'); - } - } else if ('a' <= ch && ch < 'z') { - return (createStringBuilder().append(this, 0, i).append((char) (ch + 1)).append(tail)); - } else if (ch == 'Z') { - if (i == 0) { - return createStringBuilder().append("AA").append(tail); - } else { - tail.insert(0, 'A'); - } - } else if ('A' <= ch && ch < 'Z') { - return (createStringBuilder().append(this, 0, i).append((char) (ch + 1)).append(tail)); - } else if ('0' <= ch && ch <= '9' && i == length() - 1) { - return LongValue.create(toLong() + incr); + } + + String s = toString(); + + int p = s.indexOf("::"); + + if (p < 0) { + return new CallbackFunction(env, s); + } else { + String className = s.substring(0, p); + String methodName = s.substring(p + 2); + + QuercusClass cl = env.findClass(className); + + if (cl == null) { + env.warning(L.l("can't find class {0}", + className)); + + return super.toCallable(env); } - } - - return createStringBuilder().append(tail.toString()); - } else if (getValueType().isLongAdd()) { - return LongValue.create(toLong() + incr); - } else { - return this; - } - } - - /** - * Adds to the following value. - */ - @Override - public Value add(long rValue) { - if (getValueType().isLongAdd()) { - return LongValue.create(toLong() + rValue); - } - - return DoubleValue.create(toDouble() + rValue); - } - - /** - * Adds to the following value. - */ - @Override - public Value sub(long rValue) { - if (getValueType().isLongAdd()) { - return LongValue.create(toLong() - rValue); - } - - return DoubleValue.create(toDouble() - rValue); - } - - /* - * Bit and. - */ - @Override - public Value bitAnd(Value rValue) { - if (rValue.isString()) { - StringValue rStr = (StringValue) rValue; - - int len = Math.min(length(), rValue.length()); - StringValue sb = createStringBuilder(); - - for (int i = 0; i < len; i++) { - char l = charAt(i); - char r = rStr.charAt(i); - - sb.appendByte(l & r); - } - - return sb; - } else { - return LongValue.create(toLong() & rValue.toLong()); - } - } - - /* - * Bit or. - */ - @Override - public Value bitOr(Value rValue) { - if (rValue.isString()) { - StringValue rStr = (StringValue) rValue; - - int len = Math.min(length(), rValue.length()); - StringValue sb = createStringBuilder(); - - for (int i = 0; i < len; i++) { - char l = charAt(i); - char r = rStr.charAt(i); - - sb.appendByte(l | r); - } - - if (len != length()) { - sb.append(substring(len)); - } else if (len != rStr.length()) { - sb.append(rStr.substring(len)); - } - - return sb; - } else { - return LongValue.create(toLong() | rValue.toLong()); - } - } - - /* - * Bit xor. - */ - @Override - public Value bitXor(Value rValue) { - if (rValue.isString()) { - StringValue rStr = rValue.toStringValue(); - - int len = Math.min(length(), rValue.length()); - StringValue sb = createStringBuilder(); - - for (int i = 0; i < len; i++) { - char l = charAt(i); - char r = rStr.charAt(i); - - sb.appendByte(l ^ r); - } - - return sb; - } else { - return LongValue.create(toLong() ^ rValue.toLong()); - } - } - - /** - * Serializes the value. - */ - @Override - public void serialize(Env env, StringBuilder sb) { - sb.append("s:"); - sb.append(length()); - sb.append(":\""); - sb.append(toString()); - sb.append("\";"); - } - - /** - * Encodes the value in JSON. - */ - @Override - public void jsonEncode(Env env, StringValue sb) { - sb.append('"'); - - int len = length(); - for (int i = 0; i < len; i++) { - char c = charAt(i); - - switch (c) { - case '\b': - sb.append('\\'); - sb.append('b'); - break; - case '\f': - sb.append('\\'); - sb.append('f'); - break; - case '\n': - sb.append('\\'); - sb.append('n'); - break; - case '\r': - sb.append('\\'); - sb.append('r'); - break; - case '\t': - sb.append('\\'); - sb.append('t'); - break; - case '\\': - sb.append('\\'); - sb.append('\\'); - break; - case '"': - sb.append('\\'); - sb.append('"'); - break; - case '/': - sb.append('\\'); - sb.append('/'); - break; - default: - if (c <= 0x1f) { - addUnicode(sb, c); - } else if (c < 0x80) { - sb.append(c); - } else if ((c & 0xe0) == 0xc0 && i + 1 < len) { - int c1 = charAt(i + 1); - i++; - - int ch = ((c & 0x1f) << 6) + (c1 & 0x3f); - - addUnicode(sb, ch); - } else if ((c & 0xf0) == 0xe0 && i + 2 < len) { - int c1 = charAt(i + 1); - int c2 = charAt(i + 2); - - i += 2; - - int ch = ((c & 0x0f) << 12) + ((c1 & 0x3f) << 6) + (c2 & 0x3f); - - addUnicode(sb, ch); - } else { - // technically illegal - addUnicode(sb, c); - } - - break; - } - } - - sb.append('"'); - } - - private void addUnicode(StringValue sb, int c) { - sb.append('\\'); - sb.append('u'); - - int d = (c >> 12) & 0xf; - if (d < 10) { - sb.append((char) ('0' + d)); - } else { - sb.append((char) ('a' + d - 10)); - } - - d = (c >> 8) & 0xf; - if (d < 10) { - sb.append((char) ('0' + d)); - } else { - sb.append((char) ('a' + d - 10)); - } - - d = (c >> 4) & 0xf; - if (d < 10) { - sb.append((char) ('0' + d)); - } else { - sb.append((char) ('a' + d - 10)); - } - - d = (c) & 0xf; - if (d < 10) { - sb.append((char) ('0' + d)); - } else { - sb.append((char) ('a' + d - 10)); - } - } - - /* - * Returns a value to be used as a key for the deserialize cache. - */ - /* - public StringValue toSerializeKey() - { - if (length() <= 4096) - return this; - - try { - MessageDigest md = MessageDigest.getInstance("SHA1"); - - byte []buffer = toBytes(); - - md.update(buffer, 0, buffer.length); - - //XXX: create a special serialize type? - return new StringBuilderValue(md.digest()); - - } catch (NoSuchAlgorithmException e) { - throw new QuercusException(e); - } - } - */ - // - // append code - // - /** - * Append a Java string to the value. - */ - public StringValue append(String s) { - throw new UnsupportedOperationException(getClass().getName()); - } - - /** - * Append a Java string to the value. - */ - public StringValue append(String s, int start, int end) { - throw new UnsupportedOperationException(getClass().getName()); - } - - /** - * Append a Java buffer to the value. - */ - public StringValue append(char[] buf, int offset, int length) { - throw new UnsupportedOperationException(getClass().getName()); - } - - /** - * Append a Java double to the value. - */ - public StringValue append(char[] buf) { - return append(buf, 0, buf.length); - } - - /** - * Append a Java buffer to the value. - */ - public StringValue append(CharSequence buf, int head, int tail) { - throw new UnsupportedOperationException(getClass().getName()); - } - - /** - * Append a Java buffer to the value. - */ - public StringValue append(StringBuilderValue sb, int head, int tail) { - return append((CharSequence) sb, head, tail); - } - - /** - * Append a Java buffer to the value. - */ - public StringValue append(UnicodeBuilderValue sb, int head, int tail) { - return append((CharSequence) sb, head, tail); - } - - /* - * Appends a Unicode string to the value. - * - * @param str should be a Unicode string - * @param charset to decode string from - */ - public StringValue append(Env env, StringValue unicodeStr, String charset) { - if (!unicodeStr.isUnicode()) { - return append(unicodeStr); - } - - try { - byte[] bytes = unicodeStr.toString().getBytes(charset); - - append(bytes); - return this; - - } catch (UnsupportedEncodingException e) { - env.warning(e); - - return append(unicodeStr); - } - } - - /** - * Append a Java char to the value. - */ - public StringValue append(char v) { - throw new UnsupportedOperationException(getClass().getName()); - } - - /** - * Append a Java boolean to the value. - */ - public StringValue append(boolean v) { - return append(v ? "true" : "false"); - } - - /** - * Append a Java long to the value. - */ - public StringValue append(long v) { - return append(String.valueOf(v)); - } - - /** - * Append a Java double to the value. - */ - public StringValue append(double v) { - return append(String.valueOf(v)); - } - - /** - * Append a Java value to the value. - */ - public StringValue append(Object v) { - return append(String.valueOf(v)); - } - - /** - * Append a Java value to the value. - */ - public StringValue append(Value v) { - throw new UnsupportedOperationException(getClass().getName()); - } - - /** - * Ensure enough append capacity. - */ - public void ensureAppendCapacity(int size) { - throw new UnsupportedOperationException(getClass().getName()); - } - - /** - * Append a byte buffer to the value. - */ - public StringValue append(byte[] buf, int offset, int length) { - throw new UnsupportedOperationException(getClass().getName()); - } - - /** - * Append a byte buffer to the value. - */ - public StringValue append(byte[] buf) { - return append(buf, 0, buf.length); - } - - /** - * Append a byte buffer to the value. - */ - public StringValue appendUtf8(byte[] buf, int offset, int length) { - throw new UnsupportedOperationException(getClass().getName()); - } - - /** - * Append a byte buffer to the value. - */ - public StringValue appendUtf8(byte[] buf) { - return appendUtf8(buf, 0, buf.length); - } - - /** - * Append to a string builder. - */ - @Override - public StringValue appendTo(UnicodeBuilderValue sb) { - int length = length(); - - for (int i = 0; i < length; i++) { - sb.append(charAt(i)); - } - - return this; - } - - /** - * Append a Java boolean to the value. - */ - public StringValue appendUnicode(boolean v) { - return append(v ? "true" : "false"); - } - - /** - * Append a Java long to the value. - */ - public StringValue appendUnicode(long v) { - return append(String.valueOf(v)); - } - - /** - * Append a Java double to the value. - */ - public StringValue appendUnicode(double v) { - return append(String.valueOf(v)); - } - - /** - * Append a Java value to the value. - */ - public StringValue appendUnicode(Object v) { - return append(String.valueOf(v)); - } - - /** - * Append a Java char, possibly converting to a unicode string - */ - public StringValue appendUnicode(char v) { - return append(v); - } - - /** - * Append a Java char buffer, possibly converting to a unicode string - */ - public StringValue appendUnicode(char[] buffer, int offset, int length) { - return append(buffer, offset, length); - } - - /** - * Append a Java char buffer, possibly converting to a unicode string - */ - public StringValue appendUnicode(char[] buffer) { - return append(buffer); - } - - /** - * Append a Java char buffer, possibly converting to a unicode string - */ - public StringValue appendUnicode(String value) { - return append(value); - } - - /** - * Append a Java char buffer, possibly converting to a unicode string - */ - public StringValue appendUnicode(String value, int offset, int length) { - return append(value, offset, length); - } - - /** - * Append a Java char buffer, possibly converting to a unicode string - */ - public StringValue appendUnicode(Value value) { - return append(value); - } - - /** - * Append a Java char buffer, possibly converting to a unicode string - */ - public StringValue appendUnicode(Value v1, Value v2) { - return append(v1).append(v2); - } - - /** - * Append a Java byte to the value without conversions. - */ - public StringValue appendByte(int v) { - throw new UnsupportedOperationException(getClass().getName()); - } - - /** - * Append a Java String to the value without conversions. - */ - public StringValue appendBytes(String s) { - StringValue sb = this; - - for (int i = 0; i < s.length(); i++) { - sb = sb.appendByte(s.charAt(i)); - } - - return sb; - } - - /** - * Append a Java String to the value without conversions. - */ - public StringValue appendBytes(StringValue s) { - StringValue sb = this; - - for (int i = 0; i < s.length(); i++) { - sb = sb.appendByte(s.charAt(i)); - } - - return sb; - } - - /** - * Append a Java char[] to the value without conversions. - */ - public StringValue appendBytes(char[] buf, int offset, int length) { - StringValue sb = this; - int end = Math.min(buf.length, offset + length); - - while (offset < end) { - sb = sb.appendByte(buf[offset++]); - } - - return sb; - } - - /** - * Append Java bytes to the value without conversions. - */ - public StringValue appendBytes(byte[] bytes, int offset, int end) { - StringValue sb = this; - - while (offset < end) { - sb = sb.appendByte(bytes[offset++]); - } - - return sb; - } - - /** - * Append from a temp buffer list - */ - public StringValue append(TempBuffer ptr) { - for (; ptr != null; ptr = ptr.getNext()) { - append(ptr.getBuffer(), 0, ptr.getLength()); - } - - return this; - } - - /** - * Append from a read stream - */ - public StringValue append(Reader reader) - throws IOException { - int ch; - - while ((ch = reader.read()) >= 0) { - append((char) ch); - } - - return this; - } - - /** - * Append from a read stream - */ - public StringValue append(Reader reader, long length) - throws IOException { - int ch; - - while (length-- > 0 && (ch = reader.read()) >= 0) { - append((char) ch); - } - - return this; - } - - /** - * Append from an input stream, using InputStream.read semantics, - * i.e. just call is.read once even if more data is available. - */ - public int appendRead(InputStream is, long length) { - TempBuffer tBuf = TempBuffer.allocate(); - - try { - byte[] buffer = tBuf.getBuffer(); - int sublen = buffer.length; - if (length < sublen) { - sublen = (int) length; - } - - sublen = is.read(buffer, 0, sublen); - - if (sublen > 0) { - append(buffer, 0, sublen); - } - - return sublen; - } catch (IOException e) { - throw new QuercusModuleException(e); - } finally { - TempBuffer.free(tBuf); - } - } - - /** - * Append from an input stream, reading from the input stream until - * end of file or the length is reached. - */ - public int appendReadAll(InputStream is, long length) { - TempBuffer tBuf = TempBuffer.allocate(); - - try { - byte[] buffer = tBuf.getBuffer(); - int readLength = 0; - - while (length > 0) { - int sublen = buffer.length; - if (length < sublen) { - sublen = (int) length; + + return new CallbackClassMethod(cl, env.createString(methodName)); + } + } + + /** + * Sets the array value, returning the new array, e.g. to handle + * string update ($a[0] = 'A'). Creates an array automatically if + * necessary. + */ + @Override + public Value append(Value index, Value value) { + if (length() == 0) { + return new ArrayValueImpl().append(index, value); + } else { + return this; + } + } + + // Operations + /** + * Returns the character at an index + */ + @Override + public Value get(Value key) { + return charValueAt(key.toLong()); + } + + /** + * Returns the character at an index + */ + @Override + public Value getArg(Value key, boolean isTop) { + // php/03ma + return charValueAt(key.toLong()); + } + + /** + * Returns the character at an index + */ + @Override + public Value charValueAt(long index) { + int len = length(); + + if (index < 0 || len <= index) { + return UnsetUnicodeValue.UNSET; + } else { + return StringValue.create(charAt((int) index)); + } + } + + /** + * sets the character at an index + */ + @Override + public Value setCharValueAt(long index, Value value) { + //XXX: need to double-check this for non-string values + + int len = length(); + + if (index < 0 || len <= index) { + return this; + } else { + return (createStringBuilder().append(this, 0, (int) index).append(value).append(this, (int) (index + 1), length())); + } + } + + /** + * Increment the following value. + */ + @Override + public Value increment(int incr) { + // php/03i6 + if (length() == 0) { + if (incr == 1) { + return createStringBuilder().append("1"); + } else { + return LongValue.MINUS_ONE; + } + } + + if (incr > 0) { + StringBuilder tail = new StringBuilder(); + + for (int i = length() - 1; i >= 0; i--) { + char ch = charAt(i); + + if (ch == 'z') { + if (i == 0) { + return createStringBuilder().append("aa").append(tail); + } else { + tail.insert(0, 'a'); + } + } else if ('a' <= ch && ch < 'z') { + return (createStringBuilder().append(this, 0, i).append((char) (ch + 1)).append(tail)); + } else if (ch == 'Z') { + if (i == 0) { + return createStringBuilder().append("AA").append(tail); + } else { + tail.insert(0, 'A'); + } + } else if ('A' <= ch && ch < 'Z') { + return (createStringBuilder().append(this, 0, i).append((char) (ch + 1)).append(tail)); + } else if ('0' <= ch && ch <= '9' && i == length() - 1) { + return LongValue.create(toLong() + incr); + } } - sublen = is.read(buffer, 0, sublen); + return createStringBuilder().append(tail.toString()); + } else if (getValueType().isLongAdd()) { + return LongValue.create(toLong() + incr); + } else { + return this; + } + } + + /** + * Adds to the following value. + */ + @Override + public Value add(long rValue) { + if (getValueType().isLongAdd()) { + return LongValue.create(toLong() + rValue); + } + + return DoubleValue.create(toDouble() + rValue); + } + + /** + * Adds to the following value. + */ + @Override + public Value sub(long rValue) { + if (getValueType().isLongAdd()) { + return LongValue.create(toLong() - rValue); + } + + return DoubleValue.create(toDouble() - rValue); + } + + /* + * Bit and. + */ + @Override + public Value bitAnd(Value rValue) { + if (rValue.isString()) { + StringValue rStr = (StringValue) rValue; + + int len = Math.min(length(), rValue.length()); + StringValue sb = createStringBuilder(); + + for (int i = 0; i < len; i++) { + char l = charAt(i); + char r = rStr.charAt(i); + + sb.appendByte(l & r); + } - if (sublen > 0) { - append(buffer, 0, sublen); - length -= sublen; - readLength += sublen; - } else { - return readLength > 0 ? readLength : -1; + return sb; + } else { + return LongValue.create(toLong() & rValue.toLong()); + } + } + + /* + * Bit or. + */ + @Override + public Value bitOr(Value rValue) { + if (rValue.isString()) { + StringValue rStr = (StringValue) rValue; + + int len = Math.min(length(), rValue.length()); + StringValue sb = createStringBuilder(); + + for (int i = 0; i < len; i++) { + char l = charAt(i); + char r = rStr.charAt(i); + + sb.appendByte(l | r); + } + + if (len != length()) { + sb.append(substring(len)); + } else if (len != rStr.length()) { + sb.append(rStr.substring(len)); + } + + return sb; + } else { + return LongValue.create(toLong() | rValue.toLong()); + } + } + + /* + * Bit xor. + */ + @Override + public Value bitXor(Value rValue) { + if (rValue.isString()) { + StringValue rStr = rValue.toStringValue(); + + int len = Math.min(length(), rValue.length()); + StringValue sb = createStringBuilder(); + + for (int i = 0; i < len; i++) { + char l = charAt(i); + char r = rStr.charAt(i); + + sb.appendByte(l ^ r); + } + + return sb; + } else { + return LongValue.create(toLong() ^ rValue.toLong()); + } + } + + /** + * Serializes the value. + */ + @Override + public void serialize(Env env, StringBuilder sb) { + sb.append("s:"); + sb.append(length()); + sb.append(":\""); + sb.append(toString()); + sb.append("\";"); + } + + /** + * Encodes the value in JSON. + */ + @Override + public void jsonEncode(Env env, StringValue sb) { + sb.append('"'); + + int len = length(); + for (int i = 0; i < len; i++) { + char c = charAt(i); + + switch (c) { + case '\b': + sb.append('\\'); + sb.append('b'); + break; + case '\f': + sb.append('\\'); + sb.append('f'); + break; + case '\n': + sb.append('\\'); + sb.append('n'); + break; + case '\r': + sb.append('\\'); + sb.append('r'); + break; + case '\t': + sb.append('\\'); + sb.append('t'); + break; + case '\\': + sb.append('\\'); + sb.append('\\'); + break; + case '"': + sb.append('\\'); + sb.append('"'); + break; + case '/': + sb.append('\\'); + sb.append('/'); + break; + default: + if (c <= 0x1f) { + addUnicode(sb, c); + } else if (c < 0x80) { + sb.append(c); + } else if ((c & 0xe0) == 0xc0 && i + 1 < len) { + int c1 = charAt(i + 1); + i++; + + int ch = ((c & 0x1f) << 6) + (c1 & 0x3f); + + addUnicode(sb, ch); + } else if ((c & 0xf0) == 0xe0 && i + 2 < len) { + int c1 = charAt(i + 1); + int c2 = charAt(i + 2); + + i += 2; + + int ch = ((c & 0x0f) << 12) + ((c1 & 0x3f) << 6) + (c2 & 0x3f); + + addUnicode(sb, ch); + } else { + // technically illegal + addUnicode(sb, c); + } + + break; } - } - - return readLength; - } catch (IOException e) { - throw new QuercusModuleException(e); - } finally { - TempBuffer.free(tBuf); - } - } - - /** - * Append from an input stream, reading from the input stream until - * end of file or the length is reached. - */ - public int appendReadAll(ReadStream is, long length) { - TempBuffer tBuf = TempBuffer.allocate(); - - try { - byte[] buffer = tBuf.getBuffer(); - int readLength = 0; - - while (length > 0) { + } + + sb.append('"'); + } + + private void addUnicode(StringValue sb, int c) { + sb.append('\\'); + sb.append('u'); + + int d = (c >> 12) & 0xf; + if (d < 10) { + sb.append((char) ('0' + d)); + } else { + sb.append((char) ('a' + d - 10)); + } + + d = (c >> 8) & 0xf; + if (d < 10) { + sb.append((char) ('0' + d)); + } else { + sb.append((char) ('a' + d - 10)); + } + + d = (c >> 4) & 0xf; + if (d < 10) { + sb.append((char) ('0' + d)); + } else { + sb.append((char) ('a' + d - 10)); + } + + d = (c) & 0xf; + if (d < 10) { + sb.append((char) ('0' + d)); + } else { + sb.append((char) ('a' + d - 10)); + } + } + + /* + * Returns a value to be used as a key for the deserialize cache. + */ + /* + public StringValue toSerializeKey() + { + if (length() <= 4096) + return this; + + try { + MessageDigest md = MessageDigest.getInstance("SHA1"); + + byte []buffer = toBytes(); + + md.update(buffer, 0, buffer.length); + + //XXX: create a special serialize type? + return new StringBuilderValue(md.digest()); + + } catch (NoSuchAlgorithmException e) { + throw new QuercusException(e); + } + } + */ + // + // append code + // + /** + * Append a Java string to the value. + */ + public StringValue append(String s) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Append a Java string to the value. + */ + public StringValue append(String s, int start, int end) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Append a Java buffer to the value. + */ + public StringValue append(char[] buf, int offset, int length) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Append a Java double to the value. + */ + public StringValue append(char[] buf) { + return append(buf, 0, buf.length); + } + + /** + * Append a Java buffer to the value. + */ + public StringValue append(CharSequence buf, int head, int tail) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Append a Java buffer to the value. + */ + public StringValue append(StringBuilderValue sb, int head, int tail) { + return append((CharSequence) sb, head, tail); + } + + /** + * Append a Java buffer to the value. + */ + public StringValue append(UnicodeBuilderValue sb, int head, int tail) { + return append((CharSequence) sb, head, tail); + } + + /* + * Appends a Unicode string to the value. + * + * @param str should be a Unicode string + * @param charset to decode string from + */ + public StringValue append(Env env, StringValue unicodeStr, String charset) { + if (!unicodeStr.isUnicode()) { + return append(unicodeStr); + } + + try { + byte[] bytes = unicodeStr.toString().getBytes(charset); + + append(bytes); + return this; + + } catch (UnsupportedEncodingException e) { + env.warning(e); + + return append(unicodeStr); + } + } + + /** + * Append a Java char to the value. + */ + public StringValue append(char v) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Append a Java boolean to the value. + */ + public StringValue append(boolean v) { + return append(v ? "true" : "false"); + } + + /** + * Append a Java long to the value. + */ + public StringValue append(long v) { + return append(String.valueOf(v)); + } + + /** + * Append a Java double to the value. + */ + public StringValue append(double v) { + return append(String.valueOf(v)); + } + + /** + * Append a Java value to the value. + */ + public StringValue append(Object v) { + return append(String.valueOf(v)); + } + + /** + * Append a Java value to the value. + */ + public StringValue append(Value v) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Ensure enough append capacity. + */ + public void ensureAppendCapacity(int size) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Append a byte buffer to the value. + */ + public StringValue append(byte[] buf, int offset, int length) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Append a byte buffer to the value. + */ + public StringValue append(byte[] buf) { + return append(buf, 0, buf.length); + } + + /** + * Append a byte buffer to the value. + */ + public StringValue appendUtf8(byte[] buf, int offset, int length) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Append a byte buffer to the value. + */ + public StringValue appendUtf8(byte[] buf) { + return appendUtf8(buf, 0, buf.length); + } + + /** + * Append to a string builder. + */ + @Override + public StringValue appendTo(UnicodeBuilderValue sb) { + int length = length(); + + for (int i = 0; i < length; i++) { + sb.append(charAt(i)); + } + + return this; + } + + /** + * Append a Java boolean to the value. + */ + public StringValue appendUnicode(boolean v) { + return append(v ? "true" : "false"); + } + + /** + * Append a Java long to the value. + */ + public StringValue appendUnicode(long v) { + return append(String.valueOf(v)); + } + + /** + * Append a Java double to the value. + */ + public StringValue appendUnicode(double v) { + return append(String.valueOf(v)); + } + + /** + * Append a Java value to the value. + */ + public StringValue appendUnicode(Object v) { + return append(String.valueOf(v)); + } + + /** + * Append a Java char, possibly converting to a unicode string + */ + public StringValue appendUnicode(char v) { + return append(v); + } + + /** + * Append a Java char buffer, possibly converting to a unicode string + */ + public StringValue appendUnicode(char[] buffer, int offset, int length) { + return append(buffer, offset, length); + } + + /** + * Append a Java char buffer, possibly converting to a unicode string + */ + public StringValue appendUnicode(char[] buffer) { + return append(buffer); + } + + /** + * Append a Java char buffer, possibly converting to a unicode string + */ + public StringValue appendUnicode(String value) { + return append(value); + } + + /** + * Append a Java char buffer, possibly converting to a unicode string + */ + public StringValue appendUnicode(String value, int offset, int length) { + return append(value, offset, length); + } + + /** + * Append a Java char buffer, possibly converting to a unicode string + */ + public StringValue appendUnicode(Value value) { + return append(value); + } + + /** + * Append a Java char buffer, possibly converting to a unicode string + */ + public StringValue appendUnicode(Value v1, Value v2) { + return append(v1).append(v2); + } + + /** + * Append a Java byte to the value without conversions. + */ + public StringValue appendByte(int v) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Append a Java String to the value without conversions. + */ + public StringValue appendBytes(String s) { + StringValue sb = this; + + for (int i = 0; i < s.length(); i++) { + sb = sb.appendByte(s.charAt(i)); + } + + return sb; + } + + /** + * Append a Java String to the value without conversions. + */ + public StringValue appendBytes(StringValue s) { + StringValue sb = this; + + for (int i = 0; i < s.length(); i++) { + sb = sb.appendByte(s.charAt(i)); + } + + return sb; + } + + /** + * Append a Java char[] to the value without conversions. + */ + public StringValue appendBytes(char[] buf, int offset, int length) { + StringValue sb = this; + int end = Math.min(buf.length, offset + length); + + while (offset < end) { + sb = sb.appendByte(buf[offset++]); + } + + return sb; + } + + /** + * Append Java bytes to the value without conversions. + */ + public StringValue appendBytes(byte[] bytes, int offset, int end) { + StringValue sb = this; + + while (offset < end) { + sb = sb.appendByte(bytes[offset++]); + } + + return sb; + } + + /** + * Append from a temp buffer list + */ + public StringValue append(TempBuffer ptr) { + for (; ptr != null; ptr = ptr.getNext()) { + append(ptr.getBuffer(), 0, ptr.getLength()); + } + + return this; + } + + /** + * Append from a read stream + */ + public StringValue append(Reader reader) + throws IOException { + int ch; + + while ((ch = reader.read()) >= 0) { + append((char) ch); + } + + return this; + } + + /** + * Append from a read stream + */ + public StringValue append(Reader reader, long length) + throws IOException { + int ch; + + while (length-- > 0 && (ch = reader.read()) >= 0) { + append((char) ch); + } + + return this; + } + + /** + * Append from an input stream, using InputStream.read semantics, + * i.e. just call is.read once even if more data is available. + */ + public int appendRead(InputStream is, long length) { + TempBuffer tBuf = TempBuffer.allocate(); + + try { + byte[] buffer = tBuf.getBuffer(); int sublen = buffer.length; if (length < sublen) { - sublen = (int) length; + sublen = (int) length; } sublen = is.read(buffer, 0, sublen); if (sublen > 0) { - append(buffer, 0, sublen); - length -= sublen; - readLength += sublen; - } else { - return readLength > 0 ? readLength : -1; + append(buffer, 0, sublen); } - } - - return readLength; - } catch (IOException e) { - throw new QuercusModuleException(e); - } finally { - TempBuffer.free(tBuf); - } - } - - /** - * Append from an input stream, using InputStream semantics, i.e - * call is.read() only once. - */ - public int appendRead(BinaryInput is, long length) { - TempBuffer tBuf = TempBuffer.allocate(); - - try { - byte[] buffer = tBuf.getBuffer(); - int sublen = buffer.length; - if (length < sublen) { - sublen = (int) length; - } else if (length > sublen) { - buffer = new byte[(int) length]; - sublen = (int) length; - } - - sublen = is.read(buffer, 0, sublen); - - if (sublen > 0) { - append(buffer, 0, sublen); - } - - return sublen; - } catch (IOException e) { - throw new QuercusModuleException(e); - } finally { - TempBuffer.free(tBuf); - } - } - - /** - * Append from an input stream, reading all available data from the - * stream. - */ - public int appendReadAll(BinaryInput is, long length) { - TempBuffer tBuf = TempBuffer.allocate(); - - try { - byte[] buffer = tBuf.getBuffer(); - int readLength = 0; - - while (length > 0) { + + return sublen; + } catch (IOException e) { + throw new QuercusModuleException(e); + } finally { + TempBuffer.free(tBuf); + } + } + + /** + * Append from an input stream, reading from the input stream until + * end of file or the length is reached. + */ + public int appendReadAll(InputStream is, long length) { + TempBuffer tBuf = TempBuffer.allocate(); + + try { + byte[] buffer = tBuf.getBuffer(); + int readLength = 0; + + while (length > 0) { + int sublen = buffer.length; + if (length < sublen) { + sublen = (int) length; + } + + sublen = is.read(buffer, 0, sublen); + + if (sublen > 0) { + append(buffer, 0, sublen); + length -= sublen; + readLength += sublen; + } else { + return readLength > 0 ? readLength : -1; + } + } + + return readLength; + } catch (IOException e) { + throw new QuercusModuleException(e); + } finally { + TempBuffer.free(tBuf); + } + } + + /** + * Append from an input stream, reading from the input stream until + * end of file or the length is reached. + */ + public int appendReadAll(ReadStream is, long length) { + TempBuffer tBuf = TempBuffer.allocate(); + + try { + byte[] buffer = tBuf.getBuffer(); + int readLength = 0; + + while (length > 0) { + int sublen = buffer.length; + if (length < sublen) { + sublen = (int) length; + } + + sublen = is.read(buffer, 0, sublen); + + if (sublen > 0) { + append(buffer, 0, sublen); + length -= sublen; + readLength += sublen; + } else { + return readLength > 0 ? readLength : -1; + } + } + + return readLength; + } catch (IOException e) { + throw new QuercusModuleException(e); + } finally { + TempBuffer.free(tBuf); + } + } + + /** + * Append from an input stream, using InputStream semantics, i.e + * call is.read() only once. + */ + public int appendRead(BinaryInput is, long length) { + TempBuffer tBuf = TempBuffer.allocate(); + + try { + byte[] buffer = tBuf.getBuffer(); int sublen = buffer.length; if (length < sublen) { - sublen = (int) length; + sublen = (int) length; + } else if (length > sublen) { + buffer = new byte[(int) length]; + sublen = (int) length; } sublen = is.read(buffer, 0, sublen); if (sublen > 0) { - append(buffer, 0, sublen); - length -= sublen; - readLength += sublen; - } else { - return readLength > 0 ? readLength : -1; + append(buffer, 0, sublen); } - } - - return readLength; - } catch (IOException e) { - throw new QuercusModuleException(e); - } finally { - TempBuffer.free(tBuf); - } - } - - /** - * Exports the value. - */ - @Override - public void varExport(StringBuilder sb) { - sb.append("'"); - - String value = toString(); - int len = value.length(); - for (int i = 0; i < len; i++) { - char ch = value.charAt(i); - - switch (ch) { - case '\'': - sb.append("\\'"); - break; - case '\\': - sb.append("\\\\"); - break; - default: - sb.append(ch); - } - } - sb.append("'"); - } - - /** - * Interns the string. - */ - /* - public StringValue intern(Quercus quercus) - { - return quercus.intern(toString()); - } - */ - // - // CharSequence - // - /** - * Returns the length of the string. - */ - @Override - public int length() { - return toString().length(); - } - - /** - * Returns the character at a particular location - */ - @Override - public char charAt(int index) { - return toString().charAt(index); - } - - /** - * Returns a subsequence - */ - @Override - public CharSequence subSequence(int start, int end) { - return new StringBuilderValue(toString().substring(start, end)); - } - - // - // java.lang.String methods - // - /** - * Returns the first index of the match string, starting from the head. - */ - public final int indexOf(CharSequence match) { - return indexOf(match, 0); - } - - /** - * Returns the first index of the match string, starting from the head. - */ - public int indexOf(CharSequence match, int head) { - int length = length(); - int matchLength = match.length(); - - if (matchLength <= 0) { - return -1; - } else if (head < 0) { - return -1; - } - - int end = length - matchLength; - char first = match.charAt(0); - - loop: - for (; head <= end; head++) { - if (charAt(head) != first) { - continue; - } - - for (int i = 1; i < matchLength; i++) { - if (charAt(head + i) != match.charAt(i)) { - continue loop; + + return sublen; + } catch (IOException e) { + throw new QuercusModuleException(e); + } finally { + TempBuffer.free(tBuf); + } + } + + /** + * Append from an input stream, reading all available data from the + * stream. + */ + public int appendReadAll(BinaryInput is, long length) { + TempBuffer tBuf = TempBuffer.allocate(); + + try { + byte[] buffer = tBuf.getBuffer(); + int readLength = 0; + + while (length > 0) { + int sublen = buffer.length; + if (length < sublen) { + sublen = (int) length; + } + + sublen = is.read(buffer, 0, sublen); + + if (sublen > 0) { + append(buffer, 0, sublen); + length -= sublen; + readLength += sublen; + } else { + return readLength > 0 ? readLength : -1; + } } - } - return head; - } + return readLength; + } catch (IOException e) { + throw new QuercusModuleException(e); + } finally { + TempBuffer.free(tBuf); + } + } + + /** + * Exports the value. + */ + @Override + public void varExport(StringBuilder sb) { + sb.append("'"); + + String value = toString(); + int len = value.length(); + for (int i = 0; i < len; i++) { + char ch = value.charAt(i); + + switch (ch) { + case '\'': + sb.append("\\'"); + break; + case '\\': + sb.append("\\\\"); + break; + default: + sb.append(ch); + } + } + sb.append("'"); + } + + /** + * Interns the string. + */ + /* + public StringValue intern(Quercus quercus) + { + return quercus.intern(toString()); + } + */ + // + // CharSequence + // + /** + * Returns the length of the string. + */ + @Override + public int length() { + return toString().length(); + } + + /** + * Returns the character at a particular location + */ + @Override + public char charAt(int index) { + return toString().charAt(index); + } + + /** + * Returns a subsequence + */ + @Override + public CharSequence subSequence(int start, int end) { + return new StringBuilderValue(toString().substring(start, end)); + } + + // + // java.lang.String methods + // + /** + * Returns the first index of the match string, starting from the head. + */ + public final int indexOf(CharSequence match) { + return indexOf(match, 0); + } + + /** + * Returns the first index of the match string, starting from the head. + */ + public int indexOf(CharSequence match, int head) { + int length = length(); + int matchLength = match.length(); + + if (matchLength <= 0) { + return -1; + } else if (head < 0) { + return -1; + } - return -1; - } + int end = length - matchLength; + char first = match.charAt(0); - /** - * Returns the last index of the match string, starting from the head. - */ - public int indexOf(char match) { - return indexOf(match, 0); - } + loop: + for (; head <= end; head++) { + if (charAt(head) != first) { + continue; + } - /** - * Returns the last index of the match string, starting from the head. - */ - public int indexOf(char match, int head) { - int length = length(); + for (int i = 1; i < matchLength; i++) { + if (charAt(head + i) != match.charAt(i)) { + continue loop; + } + } - for (; head < length; head++) { - if (charAt(head) == match) { return head; - } - } - - return -1; - } - - /** - * Returns the last index of the match string, starting from the head. - */ - public final int lastIndexOf(char match) { - return lastIndexOf(match, Integer.MAX_VALUE); - } - - /** - * Returns the last index of the match string, starting from the head. - */ - public int lastIndexOf(char match, int tail) { - int length = length(); - - if (tail >= length) { - tail = length - 1; - } - - for (; tail >= 0; tail--) { - if (charAt(tail) == match) { - return tail; - } - } - - return -1; - } - - /** - * Returns the last index of the match string, starting from the tail. - */ - public int lastIndexOf(CharSequence match) { - return lastIndexOf(match, Integer.MAX_VALUE); - } - - /** - * Returns the last index of the match string, starting from the tail. - */ - public int lastIndexOf(CharSequence match, int tail) { - int length = length(); - int matchLength = match.length(); - - if (matchLength <= 0) { - return -1; - } - if (tail < 0) { - return -1; - } - - if (tail > length - matchLength) { - tail = length - matchLength; - } - - char first = match.charAt(0); - - loop: - for (; tail >= 0; tail--) { - if (charAt(tail) != first) { - continue; - } - - for (int i = 1; i < matchLength; i++) { - if (charAt(tail + i) != match.charAt(i)) { - continue loop; + } + + return -1; + } + + /** + * Returns the last index of the match string, starting from the head. + */ + public int indexOf(char match) { + return indexOf(match, 0); + } + + /** + * Returns the last index of the match string, starting from the head. + */ + public int indexOf(char match, int head) { + int length = length(); + + for (; head < length; head++) { + if (charAt(head) == match) { + return head; + } + } + + return -1; + } + + /** + * Returns the last index of the match string, starting from the head. + */ + public final int lastIndexOf(char match) { + return lastIndexOf(match, Integer.MAX_VALUE); + } + + /** + * Returns the last index of the match string, starting from the head. + */ + public int lastIndexOf(char match, int tail) { + int length = length(); + + if (tail >= length) { + tail = length - 1; + } + + for (; tail >= 0; tail--) { + if (charAt(tail) == match) { + return tail; + } + } + + return -1; + } + + /** + * Returns the last index of the match string, starting from the tail. + */ + public int lastIndexOf(CharSequence match) { + return lastIndexOf(match, Integer.MAX_VALUE); + } + + /** + * Returns the last index of the match string, starting from the tail. + */ + public int lastIndexOf(CharSequence match, int tail) { + int length = length(); + int matchLength = match.length(); + + if (matchLength <= 0) { + return -1; + } + if (tail < 0) { + return -1; + } + + if (tail > length - matchLength) { + tail = length - matchLength; + } + + char first = match.charAt(0); + + loop: + for (; tail >= 0; tail--) { + if (charAt(tail) != first) { + continue; } - } - return tail; - } + for (int i = 1; i < matchLength; i++) { + if (charAt(tail + i) != match.charAt(i)) { + continue loop; + } + } - return -1; - } + return tail; + } - /** - * Returns true if the region matches - */ - public boolean regionMatches(int offset, - char[] mBuffer, int mOffset, int mLength) { - int length = length(); + return -1; + } - if (length < offset + mLength) { - return false; - } + /** + * Returns true if the region matches + */ + public boolean regionMatches(int offset, + char[] mBuffer, int mOffset, int mLength) { + int length = length(); - for (int i = 0; i < mLength; i++) { - if (charAt(offset + i) != mBuffer[mOffset + i]) { + if (length < offset + mLength) { return false; - } - } + } - return true; - } + for (int i = 0; i < mLength; i++) { + if (charAt(offset + i) != mBuffer[mOffset + i]) { + return false; + } + } - /** - * Returns true if the region matches - */ - public boolean regionMatches(int offset, - StringValue match, int mOffset, int mLength) { - int length = length(); + return true; + } - if (length < offset + mLength) { - return false; - } + /** + * Returns true if the region matches + */ + public boolean regionMatches(int offset, + StringValue match, int mOffset, int mLength) { + int length = length(); - for (int i = 0; i < mLength; i++) { - if (charAt(offset + i) != match.charAt(mOffset + i)) { + if (length < offset + mLength) { return false; - } - } + } - return true; - } + for (int i = 0; i < mLength; i++) { + if (charAt(offset + i) != match.charAt(mOffset + i)) { + return false; + } + } - /** - * Returns true if the region matches - */ - public boolean regionMatchesIgnoreCase(int offset, - char[] match, int mOffset, int mLength) { - int length = length(); + return true; + } - if (length < offset + mLength) { - return false; - } + /** + * Returns true if the region matches + */ + public boolean regionMatchesIgnoreCase(int offset, + char[] match, int mOffset, int mLength) { + int length = length(); - for (int i = 0; i < mLength; i++) { - char a = Character.toLowerCase(charAt(offset + i)); - char b = Character.toLowerCase(match[mOffset + i]); + if (length < offset + mLength) { + return false; + } - if (a != b) { + for (int i = 0; i < mLength; i++) { + char a = Character.toLowerCase(charAt(offset + i)); + char b = Character.toLowerCase(match[mOffset + i]); + + if (a != b) { + return false; + } + } + + return true; + } + + /** + * Returns true if the string ends with another string. + */ + public boolean endsWith(StringValue tail) { + int len = length(); + int tailLen = tail.length(); + + int offset = len - tailLen; + + if (offset < 0) { return false; - } - } + } + + for (int i = 0; i < tailLen; i++) { + if (charAt(offset + i) != tail.charAt(i)) { + return false; + } + } + + return true; + } + + /** + * Returns a StringValue substring. + */ + public StringValue substring(int head) { + return (StringValue) subSequence(head, length()); + } + + /** + * Returns a StringValue substring. + */ + public StringValue substring(int begin, int end) { + return (StringValue) subSequence(begin, end); + } + + /** + * Returns a String substring + */ + public String stringSubstring(int begin, int end) { + return substring(begin, end).toString(); + } + + /** + * Returns a character array + */ + public char[] toCharArray() { + int length = length(); + + char[] array = new char[length()]; + + getChars(0, array, 0, length); + + return array; + } + + public char[] getRawCharArray() { + return toCharArray(); + } + + /** + * Copies the chars + */ + public void getChars(int stringOffset, char[] buffer, int offset, int length) { + for (int i = 0; i < length; i++) { + buffer[offset + i] = charAt(stringOffset + i); + } + } + + /** + * Convert to lower case. + */ + public StringValue toLowerCase() { + int length = length(); + + UnicodeBuilderValue string = new UnicodeBuilderValue(length); + + char[] buffer = string.getBuffer(); + getChars(0, buffer, 0, length); + + for (int i = 0; i < length; i++) { + char ch = buffer[i]; + + if ('A' <= ch && ch <= 'Z') { + buffer[i] = (char) (ch + 'a' - 'A'); + } else if (ch < 0x80) { + } else if (Character.isUpperCase(ch)) { + buffer[i] = Character.toLowerCase(ch); + } + } + + string.setOffset(length); + + return string; + } + + /** + * Convert to lower case. + */ + public StringValue toUpperCase() { + int length = length(); + + UnicodeBuilderValue string = new UnicodeBuilderValue(length); - return true; - } + char[] buffer = string.getBuffer(); + getChars(0, buffer, 0, length); - /** - * Returns true if the string ends with another string. - */ - public boolean endsWith(StringValue tail) { - int len = length(); - int tailLen = tail.length(); + for (int i = 0; i < length; i++) { + char ch = buffer[i]; - int offset = len - tailLen; + if ('a' <= ch && ch <= 'z') { + buffer[i] = (char) (ch + 'A' - 'a'); + } else if (ch < 0x80) { + } else if (Character.isLowerCase(ch)) { + buffer[i] = Character.toUpperCase(ch); + } + } + + string.setOffset(length); + + return string; + } + + /** + * Returns a byteArrayInputStream for the value. + * See TempBufferStringValue for how this can be overriden + * + * @return InputStream + */ + @Override + public InputStream toInputStream() { + try { + //XXX: refactor so that env is passed in + return toInputStream(Env.getInstance().getRuntimeEncoding()); + } catch (UnsupportedEncodingException e) { + throw new QuercusRuntimeException(e); + } + //return new StringValueInputStream(); + } + + /** + * Returns a byte stream of chars. + * @param charset to encode chars to + */ + public InputStream toInputStream(String charset) + throws UnsupportedEncodingException { + return new ByteArrayInputStream(toString().getBytes(charset)); + } + + public Reader toSimpleReader() + throws UnsupportedEncodingException { + return new SimpleStringValueReader(this); + } + + /** + * Returns a char stream. + * XXX: when decoding fails + * + * @param charset to decode bytes by + */ + public Reader toReader(String charset) + throws UnsupportedEncodingException { + byte[] bytes = toBytes(); + + return new InputStreamReader(new ByteArrayInputStream(bytes), charset); + } + + public byte[] toBytes() { + throw new UnsupportedOperationException(); + } + + /** + * Converts to a unicode value. + */ + @Override + public StringValue toUnicode(Env env) { + return this; + } + + /** + * Decodes from charset and returns UnicodeValue. + * + * @param env + * @param charset + */ + public StringValue toUnicodeValue(Env env, String charset) { + StringValue sb = new UnicodeBuilderValue(); + + Decoder decoder = Decoder.create(charset); + + sb.append(decoder.decode(env, this)); + + return sb; + } + + /** + * Decodes from charset and returns UnicodeValue. + * + * @param env + * @param charset + */ + public StringValue convertToUnicode(Env env, String charset) { + UnicodeBuilderValue sb = new UnicodeBuilderValue(); + + Decoder decoder = Decoder.create(charset); + decoder.setAllowMalformedOut(true); + + sb.append(decoder.decode(env, this)); + + return sb; + } + + /** + * Converts to a string builder + */ + @Override + public StringValue toStringBuilder(Env env) { + return createStringBuilder().append(this); + } + + /** + * Return true if the array value is set + */ + @Override + public boolean isset(Value indexV) { + int index = indexV.toInt(); + int len = length(); + + return 0 <= index && index < len; + } + + /** + * Writes to a stream + */ + public void writeTo(OutputStream os) { + try { + int len = length(); + + for (int i = 0; i < len; i++) { + os.write(charAt(i)); + } + } catch (IOException e) { + throw new QuercusModuleException(e); + } + } + + /** + * Calculates CRC32 value. + */ + public long getCrc32Value() { + CRC32 crc = new CRC32(); + + int length = length(); + + for (int i = 0; i < length; i++) { + crc.update((byte) charAt(i)); + } + + return crc.getValue() & 0xffffffff; + } + + // + // ByteAppendable methods + // + @Override + public void write(int value) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Appends buffer to the ByteAppendable. + */ + @Override + public void write(byte[] buffer, int offset, int len) { + throw new UnsupportedOperationException(getClass().getName()); + } + + // + // java.lang.Object methods + // + /** + * Returns the hash code. + */ + @Override + public int hashCode() { + int hash = 37; + + int length = length(); + + for (int i = 0; i < length; i++) { + hash = 65521 * hash + charAt(i); + } + //System.err.println("Hash code for " + this + " = " + hash); + return hash; + } + + /** + * Returns the case-insensitive hash code + */ + public int hashCodeCaseInsensitive() { + int hash = 37; + + int length = length(); + + for (int i = length - 1; i >= 0; i--) { + int ch = charAt(i); + + if ('A' <= ch && ch <= 'Z') { + ch = ch + 'a' - 'A'; + } - if (offset < 0) { - return false; - } + hash = 65521 * hash + ch; + } + //System.err.println("Hash insensitive code for " + this + " = " + hash); + return hash; + } + + /** + * Test for equality + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (!(o instanceof StringValue)) { + return false; + } + + StringValue s = (StringValue) o; - for (int i = 0; i < tailLen; i++) { - if (charAt(offset + i) != tail.charAt(i)) { + if (s.isUnicode() != isUnicode()) { return false; - } - } - - return true; - } - - /** - * Returns a StringValue substring. - */ - public StringValue substring(int head) { - return (StringValue) subSequence(head, length()); - } - - /** - * Returns a StringValue substring. - */ - public StringValue substring(int begin, int end) { - return (StringValue) subSequence(begin, end); - } - - /** - * Returns a String substring - */ - public String stringSubstring(int begin, int end) { - return substring(begin, end).toString(); - } - - /** - * Returns a character array - */ - public char[] toCharArray() { - int length = length(); - - char[] array = new char[length()]; - - getChars(0, array, 0, length); - - return array; - } - - public char[] getRawCharArray() { - return toCharArray(); - } - - /** - * Copies the chars - */ - public void getChars(int stringOffset, char[] buffer, int offset, int length) { - for (int i = 0; i < length; i++) { - buffer[offset + i] = charAt(stringOffset + i); - } - } - - /** - * Convert to lower case. - */ - public StringValue toLowerCase() { - int length = length(); - - UnicodeBuilderValue string = new UnicodeBuilderValue(length); - - char[] buffer = string.getBuffer(); - getChars(0, buffer, 0, length); - - for (int i = 0; i < length; i++) { - char ch = buffer[i]; - - if ('A' <= ch && ch <= 'Z') { - buffer[i] = (char) (ch + 'a' - 'A'); - } else if (ch < 0x80) { - } else if (Character.isUpperCase(ch)) { - buffer[i] = Character.toLowerCase(ch); - } - } - - string.setOffset(length); - - return string; - } - - /** - * Convert to lower case. - */ - public StringValue toUpperCase() { - int length = length(); - - UnicodeBuilderValue string = new UnicodeBuilderValue(length); - - char[] buffer = string.getBuffer(); - getChars(0, buffer, 0, length); - - for (int i = 0; i < length; i++) { - char ch = buffer[i]; - - if ('a' <= ch && ch <= 'z') { - buffer[i] = (char) (ch + 'A' - 'a'); - } else if (ch < 0x80) { - } else if (Character.isLowerCase(ch)) { - buffer[i] = Character.toUpperCase(ch); - } - } - - string.setOffset(length); - - return string; - } - - /** - * Returns a byteArrayInputStream for the value. - * See TempBufferStringValue for how this can be overriden - * - * @return InputStream - */ - @Override - public InputStream toInputStream() { - try { - //XXX: refactor so that env is passed in - return toInputStream(Env.getInstance().getRuntimeEncoding()); - } catch (UnsupportedEncodingException e) { - throw new QuercusRuntimeException(e); - } - //return new StringValueInputStream(); - } - - /** - * Returns a byte stream of chars. - * @param charset to encode chars to - */ - public InputStream toInputStream(String charset) - throws UnsupportedEncodingException { - return new ByteArrayInputStream(toString().getBytes(charset)); - } - - public Reader toSimpleReader() - throws UnsupportedEncodingException { - return new SimpleStringValueReader(this); - } - - /** - * Returns a char stream. - * XXX: when decoding fails - * - * @param charset to decode bytes by - */ - public Reader toReader(String charset) - throws UnsupportedEncodingException { - byte[] bytes = toBytes(); - - return new InputStreamReader(new ByteArrayInputStream(bytes), charset); - } - - public byte[] toBytes() { - throw new UnsupportedOperationException(); - } - - /** - * Converts to a unicode value. - */ - @Override - public StringValue toUnicode(Env env) { - return this; - } - - /** - * Decodes from charset and returns UnicodeValue. - * - * @param env - * @param charset - */ - public StringValue toUnicodeValue(Env env, String charset) { - StringValue sb = new UnicodeBuilderValue(); - - Decoder decoder = Decoder.create(charset); - - sb.append(decoder.decode(env, this)); - - return sb; - } - - /** - * Decodes from charset and returns UnicodeValue. - * - * @param env - * @param charset - */ - public StringValue convertToUnicode(Env env, String charset) { - UnicodeBuilderValue sb = new UnicodeBuilderValue(); - - Decoder decoder = Decoder.create(charset); - decoder.setAllowMalformedOut(true); - - sb.append(decoder.decode(env, this)); - - return sb; - } - - /** - * Converts to a string builder - */ - @Override - public StringValue toStringBuilder(Env env) { - return createStringBuilder().append(this); - } - - /** - * Return true if the array value is set - */ - @Override - public boolean isset(Value indexV) { - int index = indexV.toInt(); - int len = length(); - - return 0 <= index && index < len; - } - - /** - * Writes to a stream - */ - public void writeTo(OutputStream os) { - try { - int len = length(); - - for (int i = 0; i < len; i++) { - os.write(charAt(i)); - } - } catch (IOException e) { - throw new QuercusModuleException(e); - } - } - - /** - * Calculates CRC32 value. - */ - public long getCrc32Value() { - CRC32 crc = new CRC32(); - - int length = length(); - - for (int i = 0; i < length; i++) { - crc.update((byte) charAt(i)); - } - - return crc.getValue() & 0xffffffff; - } - - // - // ByteAppendable methods - // - @Override - public void write(int value) { - throw new UnsupportedOperationException(getClass().getName()); - } - - /** - * Appends buffer to the ByteAppendable. - */ - @Override - public void write(byte[] buffer, int offset, int len) { - throw new UnsupportedOperationException(getClass().getName()); - } - - // - // java.lang.Object methods - // - /** - * Returns the hash code. - */ - @Override - public int hashCode() { - int hash = 37; - - int length = length(); - - for (int i = 0; i < length; i++) { - hash = 65521 * hash + charAt(i); - } - - return hash; - } - - /** - * Returns the case-insensitive hash code - */ - public int hashCodeCaseInsensitive() { - int hash = 37; - - int length = length(); - - for (int i = length - 1; i >= 0; i--) { - int ch = charAt(i); - - if ('A' <= ch && ch <= 'Z') { - ch = ch + 'a' - 'A'; - } - - hash = 65521 * hash + ch; - } - - return hash; - } - - /** - * Test for equality - */ - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } else if (!(o instanceof StringValue)) { - return false; - } - - StringValue s = (StringValue) o; - - if (s.isUnicode() != isUnicode()) { - return false; - } - - int aLength = length(); - int bLength = s.length(); - - if (aLength != bLength) { - return false; - } - - for (int i = aLength - 1; i >= 0; i--) { - if (charAt(i) != s.charAt(i)) { + } + + int aLength = length(); + int bLength = s.length(); + + if (aLength != bLength) { return false; - } - } - - return true; - } - - /** - * Test for equality - */ - public boolean equalsIgnoreCase(Object o) { - if (this == o) { - return true; - } else if (!(o instanceof StringValue)) { - return false; - } - - StringValue s = (StringValue) o; - - if (s.isUnicode() != isUnicode()) { - return false; - } - - int aLength = length(); - int bLength = s.length(); - - if (aLength != bLength) { - return false; - } - - for (int i = aLength - 1; i >= 0; i--) { - int chA = charAt(i); - int chB = s.charAt(i); - - if (chA == chB) { - } else { - if ('A' <= chA && chA <= 'Z') { - chA += 'a' - 'A'; - } + } - if ('A' <= chB && chB <= 'Z') { - chB += 'a' - 'A'; + for (int i = aLength - 1; i >= 0; i--) { + if (charAt(i) != s.charAt(i)) { + return false; } + } + + return true; + } + + /** + * Test for equality + */ + public boolean equalsIgnoreCase(Object o) { + //System.err.println("equalsIgnoreCase " + o + " to this " + this); + if (this == o) { + return true; + } else if (!(o instanceof StringValue)) { + return false; + } + + StringValue s = (StringValue) o; + +// if (s.isUnicode() != isUnicode()) { +// return false; +// } - if (chA != chB) { - return false; + int aLength = length(); + int bLength = s.length(); + + if (aLength != bLength) { + return false; + } + + for (int i = aLength - 1; i >= 0; i--) { + int chA = charAt(i); + int chB = s.charAt(i); + + if (chA == chB) { + } else { + if ('A' <= chA && chA <= 'Z') { + chA += 'a' - 'A'; + } + + if ('A' <= chB && chB <= 'Z') { + chB += 'a' - 'A'; + } + + if (chA != chB) { + return false; + } } - } - } - - return true; - } - - // - // Java generator code - // - /** - * Generates code to recreate the expression. - * - * @param out the writer to the Java source code. - */ - @Override - public void generate(PrintWriter out) - throws IOException { - // max JVM constant string length - int maxSublen = 0xFFFE; - - int len = length(); - - String className = getClass().getSimpleName(); - - if (len == 1) { - out.print(className + ".create('"); - printJavaChar(out, charAt(0)); - out.print("')"); - } else if (len < maxSublen) { - out.print("new " + className + "(\""); - printJavaString(out, this); - out.print("\")"); - } else { - out.print("((" + className + ") (new " + className + "(\""); - - // php/313u - for (int i = 0; i < len; i += maxSublen) { - if (i != 0) { - out.print("\").append(\""); + } + + return true; + } + + // + // Java generator code + // + /** + * Generates code to recreate the expression. + * + * @param out the writer to the Java source code. + */ + @Override + public void generate(PrintWriter out) + throws IOException { + // max JVM constant string length + int maxSublen = 0xFFFE; + + int len = length(); + + String className = getClass().getSimpleName(); + + if (len == 1) { + out.print(className + ".create('"); + printJavaChar(out, charAt(0)); + out.print("')"); + } else if (len < maxSublen) { + out.print("new " + className + "(\""); + printJavaString(out, this); + out.print("\")"); + } else { + out.print("((" + className + ") (new " + className + "(\""); + + // php/313u + for (int i = 0; i < len; i += maxSublen) { + if (i != 0) { + out.print("\").append(\""); + } + + printJavaString(out, substring(i, Math.min(i + maxSublen, len))); } - printJavaString(out, substring(i, Math.min(i + maxSublen, len))); - } + out.print("\")))"); + } + } - out.print("\")))"); - } - } + @Override + abstract public String toDebugString(); - @Override - abstract public String toDebugString(); + @Override + abstract public void varDumpImpl(Env env, + WriteStream out, + int depth, + IdentityHashMap valueSet) + throws IOException; - @Override - abstract public void varDumpImpl(Env env, - WriteStream out, - int depth, - IdentityHashMap valueSet) - throws IOException; + class StringValueInputStream extends java.io.InputStream { - class StringValueInputStream extends java.io.InputStream { + private final int _length; + private int _index; - private final int _length; - private int _index; + StringValueInputStream() { + _length = length(); + } - StringValueInputStream() { - _length = length(); - } + /** + * Reads the next byte. + */ + @Override + public int read() { + if (_index < _length) { + return charAt(_index++); + } else { + return -1; + } + } - /** - * Reads the next byte. - */ - @Override - public int read() { - if (_index < _length) { - return charAt(_index++); - } else { - return -1; - } - } + /** + * Reads into a buffer. + */ + @Override + public int read(byte[] buffer, int offset, int length) { + int sublen = _length - _index; - /** - * Reads into a buffer. - */ - @Override - public int read(byte[] buffer, int offset, int length) { - int sublen = _length - _index; + if (length < sublen) { + sublen = length; + } - if (length < sublen) { - sublen = length; - } + if (sublen <= 0) { + return -1; + } - if (sublen <= 0) { - return -1; - } + int index = _index; - int index = _index; + for (int i = 0; i < sublen; i++) { + buffer[offset + i] = (byte) charAt(index + i); + } - for (int i = 0; i < sublen; i++) { - buffer[offset + i] = (byte) charAt(index + i); - } + _index += sublen; - _index += sublen; + return sublen; + } + } - return sublen; - } - } + static class SimpleStringValueReader extends Reader { - static class SimpleStringValueReader extends Reader { + StringValue _str; + int _index; + int _length; - StringValue _str; - int _index; - int _length; + SimpleStringValueReader(StringValue s) { + _str = s; + _length = s.length(); + } - SimpleStringValueReader(StringValue s) { - _str = s; - _length = s.length(); - } + @Override + public int read() { + if (_index >= _length) { + return -1; + } else { + return _str.charAt(_index++); + } + } - @Override - public int read() { - if (_index >= _length) { - return -1; - } else { - return _str.charAt(_index++); - } - } - - @Override - public int read(char[] buf, int off, int len) { - if (_index >= _length) { - return -1; - } + @Override + public int read(char[] buf, int off, int len) { + if (_index >= _length) { + return -1; + } - int i = 0; - len = Math.min(_length - _index, len); + int i = 0; + len = Math.min(_length - _index, len); - for (; i < len; i++) { - buf[off + i] = _str.charAt(i + _index++); - } + for (; i < len; i++) { + buf[off + i] = _str.charAt(i + _index++); + } - return i; - } + return i; + } - @Override - public void close() - throws IOException { - } - } + @Override + public void close() + throws IOException { + } + } } diff --git a/quercus/src/main/java/com/caucho/quercus/env/UnicodeBuilderValue.java b/quercus/src/main/java/com/caucho/quercus/env/UnicodeBuilderValue.java index fcf6489..d32b819 100644 --- a/quercus/src/main/java/com/caucho/quercus/env/UnicodeBuilderValue.java +++ b/quercus/src/main/java/com/caucho/quercus/env/UnicodeBuilderValue.java @@ -39,1319 +39,1320 @@ public class UnicodeBuilderValue extends UnicodeValue { - public static final UnicodeBuilderValue EMPTY = new UnicodeBuilderValue(""); - private static final UnicodeBuilderValue[] CHAR_STRINGS; - private char[] _buffer; - private int _length; - protected boolean _isCopy; - private int _hashCode; + public static final UnicodeBuilderValue EMPTY = new UnicodeBuilderValue(""); + private static final UnicodeBuilderValue[] CHAR_STRINGS; + private char[] _buffer; + private int _length; + protected boolean _isCopy; + private int _hashCode; - public UnicodeBuilderValue() { - _buffer = new char[MIN_LENGTH]; - } + public UnicodeBuilderValue() { + _buffer = new char[MIN_LENGTH]; + } - public UnicodeBuilderValue(int capacity) { - if (capacity < MIN_LENGTH) { - capacity = MIN_LENGTH; - } + public UnicodeBuilderValue(int capacity) { + if (capacity < MIN_LENGTH) { + capacity = MIN_LENGTH; + } - _buffer = new char[capacity]; - } + _buffer = new char[capacity]; + } - public UnicodeBuilderValue(String s) { - int len = s.length(); + public UnicodeBuilderValue(String s) { + int len = s.length(); - _buffer = new char[len]; - _length = len; - - s.getChars(0, len, _buffer, 0); - } - - public UnicodeBuilderValue(char[] buffer, int offset, int length) { - _buffer = new char[length]; - _length = length; - - System.arraycopy(buffer, offset, _buffer, 0, length); - } - - public UnicodeBuilderValue(char[] buffer) { - this(buffer, 0, buffer.length); - } - - public UnicodeBuilderValue(char[] buffer, int length) { - this(buffer, 0, length); - } - - public UnicodeBuilderValue(Character[] buffer) { - int length = buffer.length; - - _buffer = new char[length]; - _length = length; - - for (int i = 0; i < length; i++) { - _buffer[i] = buffer[i].charValue(); - } - } - - public UnicodeBuilderValue(char ch) { - _buffer = new char[1]; - _length = 1; - - _buffer[0] = ch; - } - - public UnicodeBuilderValue(char[] s, Value v1) { - int len = s.length; - - int bufferLength = MIN_LENGTH; - while (bufferLength < len) { - bufferLength *= 2; - } - - _buffer = new char[bufferLength]; - - _length = len; - - System.arraycopy(s, 0, _buffer, 0, len); - - v1.appendTo(this); - } - - public UnicodeBuilderValue(Value v1) { - _buffer = new char[MIN_LENGTH]; - - v1.appendTo(this); - } - - public UnicodeBuilderValue(UnicodeBuilderValue v) { - if (v._isCopy) { - _buffer = new char[v._buffer.length]; - System.arraycopy(v._buffer, 0, _buffer, 0, v._length); - _length = v._length; - } else { - _buffer = v._buffer; - _length = v._length; - v._isCopy = true; - } - } - - public UnicodeBuilderValue(StringBuilderValue v, boolean isCopy) { - byte[] vBuffer = v.getBuffer(); - int vOffset = v.getOffset(); - - _buffer = new char[vBuffer.length]; - - System.arraycopy(vBuffer, 0, _buffer, 0, vOffset); - - _length = vOffset; - } - - /** - * Creates the string. - */ - public static StringValue create(char value) { - if (value < CHAR_STRINGS.length) { - return CHAR_STRINGS[value]; - } else { - return new UnicodeBuilderValue(value); - } - } - - /** - * Creates a PHP string from a Java String. - * If the value is null then NullValue is returned. - */ - public static Value create(String value) { - if (value == null) { - return NullValue.NULL; - } else if (value.length() == 0) { - return UnicodeBuilderValue.EMPTY; - } else { - return new UnicodeBuilderValue(value); - } - } - - /** - * Decodes the Unicode str from charset. - * - * @param str should be a Unicode string - * @param charset to decode string from - */ - @Override - public StringValue create(Env env, StringValue str, String charset) { - return str; - } - - /** - * Decodes from charset and returns UnicodeValue. - * - * @param env - * @param charset - */ - @Override - public final StringValue convertToUnicode(Env env, String charset) { - return this; - } - - /** - * Returns true for UnicodeValue - */ - @Override - public final boolean isUnicode() { - return true; - } - - /** - * Returns the value. - */ - public final String getValue() { - return toString(); - } - - /** - * Returns the type. - */ - @Override - public String getType() { - return "string"; - } - - /** - * Returns the ValueType. - */ - @Override - public ValueType getValueType() { - return getValueType(_buffer, 0, _length); - } - - /** - * Converts to a string builder - */ - @Override - public StringValue toStringBuilder() { - return new UnicodeBuilderValue(this); - } - - /** - * Converts to a string builder - */ - @Override - public StringValue toStringBuilder(Env env) { - return new UnicodeBuilderValue(this); - } - - /** - * Converts to a string builder - */ - @Override - public StringValue toStringBuilder(Env env, Value value) { - UnicodeBuilderValue v = new UnicodeBuilderValue(this); - - value.appendTo(v); - - return v; - } - - /** - * Converts to a string builder - */ - @Override - public StringValue copyStringBuilder() { - return new UnicodeBuilderValue(this); - } - - /** - * Converts to a UnicodeValue. - */ - @Override - public final StringValue toUnicodeValue() { - return this; - } - - /** - * Converts to a UnicodeValue. - */ - @Override - public final StringValue toUnicodeValue(Env env) { - return this; - } - - /** - * Converts to a UnicodeValue in desired charset. - */ - @Override - public final StringValue toUnicodeValue(Env env, String charset) { - return this; - } - - /** - * Append a buffer to the value. - */ - @Override - public final StringValue append(byte[] buf, int offset, int length) { - if (_buffer.length < _length + length) { - ensureCapacity(_length + length); - } - - /* - Env env = Env.getInstance(); - String charset = (env != null - ? env.getRuntimeEncoding().toString() - : null); - */ - - char[] charBuffer = _buffer; - int charLength = _length; - - for (int i = 0; i < length; i++) { - // php/3jdf - charBuffer[charLength + i] = (char) (buf[offset + i] & 0xff); - } - - _length += length; - - return this; - } - - // - // append code - // - /** - * Append a Java value to the value. - */ - @Override - public StringValue append(Value v) { - v.appendTo(this); - - return this; - } - - /** - * Append a Java string to the value. - */ - @Override - public StringValue append(String s) { - int len = s.length(); - - if (_buffer.length < _length + len) { - ensureCapacity(_length + len); - } - - s.getChars(0, len, _buffer, _length); - - _length += len; - - return this; - } - - /** - * Append a Java string to the value. - */ - @Override - public StringValue append(String s, int start, int end) { - int len = Math.min(s.length(), end - start); - - if (_buffer.length < _length + len) { - ensureCapacity(_length + len); - } - - s.getChars(start, start + len, _buffer, _length); - - _length += len; - - return this; - } - - /** - * Append a Java char to the value. - */ - @Override - public StringValue append(char v) { - if (_buffer.length < _length + 1) { - ensureCapacity(_length + 1); - } - - _buffer[_length++] = v; - - return this; - } - - /** - * Append a Java buffer to the value. - */ - @Override - public StringValue append(char[] buf, int offset, int length) { - if (_buffer.length < _length + length) { - ensureCapacity(_length + length); - } - - System.arraycopy(buf, offset, _buffer, _length, length); - - _length += length; - - return this; - } - - /** - * Append a Java double to the value. - */ - @Override - public StringValue append(char[] buf) { - return append(buf, 0, buf.length); - } - - /** - * Append a Java buffer to the value. - */ - @Override - public StringValue append(CharSequence buf, int head, int tail) { - int len = tail - head; - - if (_buffer.length < _length + len) { - ensureAppendCapacity(len); - } - - char[] buffer = _buffer; - int bufferLength = _length; - - for (; head < tail; head++) { - buffer[bufferLength++] = buf.charAt(head); - } - - _length = bufferLength; - - return this; - } - - /** - * Append a Java buffer to the value. - */ - @Override - public StringValue append(UnicodeBuilderValue sb, int head, int tail) { - int len = tail - head; - - if (_buffer.length < _length + len) { - ensureAppendCapacity(len); - } - - System.arraycopy(sb._buffer, head, _buffer, _length, len); + _buffer = new char[len]; + _length = len; + + s.getChars(0, len, _buffer, 0); + } + + public UnicodeBuilderValue(char[] buffer, int offset, int length) { + _buffer = new char[length]; + _length = length; + + System.arraycopy(buffer, offset, _buffer, 0, length); + } + + public UnicodeBuilderValue(char[] buffer) { + this(buffer, 0, buffer.length); + } + + public UnicodeBuilderValue(char[] buffer, int length) { + this(buffer, 0, length); + } + + public UnicodeBuilderValue(Character[] buffer) { + int length = buffer.length; + + _buffer = new char[length]; + _length = length; + + for (int i = 0; i < length; i++) { + _buffer[i] = buffer[i].charValue(); + } + } + + public UnicodeBuilderValue(char ch) { + _buffer = new char[1]; + _length = 1; + + _buffer[0] = ch; + } + + public UnicodeBuilderValue(char[] s, Value v1) { + int len = s.length; + + int bufferLength = MIN_LENGTH; + while (bufferLength < len) { + bufferLength *= 2; + } + + _buffer = new char[bufferLength]; + + _length = len; + + System.arraycopy(s, 0, _buffer, 0, len); + + v1.appendTo(this); + } + + public UnicodeBuilderValue(Value v1) { + _buffer = new char[MIN_LENGTH]; + + v1.appendTo(this); + } + + public UnicodeBuilderValue(UnicodeBuilderValue v) { + if (v._isCopy) { + _buffer = new char[v._buffer.length]; + System.arraycopy(v._buffer, 0, _buffer, 0, v._length); + _length = v._length; + } else { + _buffer = v._buffer; + _length = v._length; + v._isCopy = true; + } + } + + public UnicodeBuilderValue(StringBuilderValue v, boolean isCopy) { + byte[] vBuffer = v.getBuffer(); + int vOffset = v.getOffset(); + + _buffer = new char[vBuffer.length]; + + System.arraycopy(vBuffer, 0, _buffer, 0, vOffset); + + _length = vOffset; + } + + /** + * Creates the string. + */ + public static StringValue create(char value) { + if (value < CHAR_STRINGS.length) { + return CHAR_STRINGS[value]; + } else { + return new UnicodeBuilderValue(value); + } + } + + /** + * Creates a PHP string from a Java String. + * If the value is null then NullValue is returned. + */ + public static Value create(String value) { + if (value == null) { + return NullValue.NULL; + } else if (value.length() == 0) { + return UnicodeBuilderValue.EMPTY; + } else { + return new UnicodeBuilderValue(value); + } + } + + /** + * Decodes the Unicode str from charset. + * + * @param str should be a Unicode string + * @param charset to decode string from + */ + @Override + public StringValue create(Env env, StringValue str, String charset) { + return str; + } + + /** + * Decodes from charset and returns UnicodeValue. + * + * @param env + * @param charset + */ + @Override + public final StringValue convertToUnicode(Env env, String charset) { + return this; + } + + /** + * Returns true for UnicodeValue + */ + @Override + public final boolean isUnicode() { + return true; + } + + /** + * Returns the value. + */ + public final String getValue() { + return toString(); + } + + /** + * Returns the type. + */ + @Override + public String getType() { + return "string"; + } + + /** + * Returns the ValueType. + */ + @Override + public ValueType getValueType() { + return getValueType(_buffer, 0, _length); + } + + /** + * Converts to a string builder + */ + @Override + public StringValue toStringBuilder() { + return new UnicodeBuilderValue(this); + } + + /** + * Converts to a string builder + */ + @Override + public StringValue toStringBuilder(Env env) { + return new UnicodeBuilderValue(this); + } + + /** + * Converts to a string builder + */ + @Override + public StringValue toStringBuilder(Env env, Value value) { + UnicodeBuilderValue v = new UnicodeBuilderValue(this); + + value.appendTo(v); + + return v; + } + + /** + * Converts to a string builder + */ + @Override + public StringValue copyStringBuilder() { + return new UnicodeBuilderValue(this); + } + + /** + * Converts to a UnicodeValue. + */ + @Override + public final StringValue toUnicodeValue() { + return this; + } + + /** + * Converts to a UnicodeValue. + */ + @Override + public final StringValue toUnicodeValue(Env env) { + return this; + } + + /** + * Converts to a UnicodeValue in desired charset. + */ + @Override + public final StringValue toUnicodeValue(Env env, String charset) { + return this; + } + + /** + * Append a buffer to the value. + */ + @Override + public final StringValue append(byte[] buf, int offset, int length) { + if (_buffer.length < _length + length) { + ensureCapacity(_length + length); + } + + /* + Env env = Env.getInstance(); + String charset = (env != null + ? env.getRuntimeEncoding().toString() + : null); + */ + + char[] charBuffer = _buffer; + int charLength = _length; + + for (int i = 0; i < length; i++) { + // php/3jdf + charBuffer[charLength + i] = (char) (buf[offset + i] & 0xff); + } + + _length += length; + + return this; + } + + // + // append code + // + /** + * Append a Java value to the value. + */ + @Override + public StringValue append(Value v) { + v.appendTo(this); + + return this; + } + + /** + * Append a Java string to the value. + */ + @Override + public StringValue append(String s) { + int len = s.length(); + + if (_buffer.length < _length + len) { + ensureCapacity(_length + len); + } + + s.getChars(0, len, _buffer, _length); + + _length += len; + + return this; + } + + /** + * Append a Java string to the value. + */ + @Override + public StringValue append(String s, int start, int end) { + int len = Math.min(s.length(), end - start); + + if (_buffer.length < _length + len) { + ensureCapacity(_length + len); + } + + s.getChars(start, start + len, _buffer, _length); + + _length += len; + + return this; + } + + /** + * Append a Java char to the value. + */ + @Override + public StringValue append(char v) { + if (_buffer.length < _length + 1) { + ensureCapacity(_length + 1); + } + + _buffer[_length++] = v; + + return this; + } + + /** + * Append a Java buffer to the value. + */ + @Override + public StringValue append(char[] buf, int offset, int length) { + if (_buffer.length < _length + length) { + ensureCapacity(_length + length); + } + + System.arraycopy(buf, offset, _buffer, _length, length); + + _length += length; + + return this; + } + + /** + * Append a Java double to the value. + */ + @Override + public StringValue append(char[] buf) { + return append(buf, 0, buf.length); + } + + /** + * Append a Java buffer to the value. + */ + @Override + public StringValue append(CharSequence buf, int head, int tail) { + int len = tail - head; + + if (_buffer.length < _length + len) { + ensureAppendCapacity(len); + } + + char[] buffer = _buffer; + int bufferLength = _length; + + for (; head < tail; head++) { + buffer[bufferLength++] = buf.charAt(head); + } + + _length = bufferLength; + + return this; + } - _length += len; - - return this; - } - - - /* - * Appends a Unicode string to the value. - * - * @param str should be a Unicode string - * @param charset to decode string from - */ - @Override - public StringValue append(Env env, StringValue unicodeStr, String charset) { - return append(unicodeStr); - } - - /** - * Append to a string builder. - */ - @Override - public StringValue appendTo(UnicodeBuilderValue sb) { - sb.append(_buffer, 0, _length); - - return sb; - } - - @Override - public StringValue append(Reader reader, long length) - throws IOException { - // php/4407 - oracle clob callback passes very long length - - int sublen = (int) Math.min(_length, length); - - try { - while (length > 0) { - if (_buffer.length < _length + sublen) { - ensureCapacity(_length + sublen); - } + /** + * Append a Java buffer to the value. + */ + @Override + public StringValue append(UnicodeBuilderValue sb, int head, int tail) { + int len = tail - head; + + if (_buffer.length < _length + len) { + ensureAppendCapacity(len); + } + + System.arraycopy(sb._buffer, head, _buffer, _length, len); + + _length += len; - int count = reader.read(_buffer, _length, sublen); + return this; + } + + + /* + * Appends a Unicode string to the value. + * + * @param str should be a Unicode string + * @param charset to decode string from + */ + @Override + public StringValue append(Env env, StringValue unicodeStr, String charset) { + return append(unicodeStr); + } + + /** + * Append to a string builder. + */ + @Override + public StringValue appendTo(UnicodeBuilderValue sb) { + sb.append(_buffer, 0, _length); + + return sb; + } + + @Override + public StringValue append(Reader reader, long length) + throws IOException { + // php/4407 - oracle clob callback passes very long length + + int sublen = (int) Math.min(_length, length); - if (count <= 0) { - break; + try { + while (length > 0) { + if (_buffer.length < _length + sublen) { + ensureCapacity(_length + sublen); + } + + int count = reader.read(_buffer, _length, sublen); + + if (count <= 0) { + break; + } + + length -= count; + _length += count; } - length -= count; - _length += count; - } - - } catch (IOException e) { - throw new QuercusModuleException(e); - } - - return this; - } - - /** - * Append a Java byte to the value without conversions. - */ - @Override - public StringValue appendByte(int v) { - if (_buffer.length < _length + 1) { - ensureCapacity(_length + 1); - } - - _buffer[_length++] = (char) v; - - return this; - } - - /** - * Returns true if the value is empty. - */ - @Override - public final boolean isEmpty() { - return _length == 0 || _length == 1 && _buffer[0] == '0'; - } - - /** - * Converts to a key. - */ - @Override - public Value toKey() { - char[] buffer = _buffer; - int len = _length; - - if (len == 0) { - return this; - } - - int sign = 1; - long value = 0; - - int i = 0; - int ch = buffer[i]; - if (ch == '-') { - sign = -1; - i++; - } - - for (; i < len; i++) { - ch = buffer[i]; - - if ('0' <= ch && ch <= '9') { - value = 10 * value + ch - '0'; - } else { + } catch (IOException e) { + throw new QuercusModuleException(e); + } + + return this; + } + + /** + * Append a Java byte to the value without conversions. + */ + @Override + public StringValue appendByte(int v) { + if (_buffer.length < _length + 1) { + ensureCapacity(_length + 1); + } + + _buffer[_length++] = (char) v; + + return this; + } + + /** + * Returns true if the value is empty. + */ + @Override + public final boolean isEmpty() { + return _length == 0 || _length == 1 && _buffer[0] == '0'; + } + + /** + * Converts to a key. + */ + @Override + public Value toKey() { + char[] buffer = _buffer; + int len = _length; + + if (len == 0) { return this; - } - } - - return LongValue.create(sign * value); - } - - /** - * Converts to a byte array, with no consideration of character encoding. - * Each character becomes one byte, characters with values above 255 are - * not correctly preserved. - */ - @Override - public final byte[] toBytes() { - byte[] bytes = new byte[_length]; - - for (int i = 0; i < _length; i++) { - bytes[i] = (byte) (_buffer[i] & 0xFF); - } - - return bytes; - } - - // - // Operations - // - /** - * Returns the character at an index - */ - @Override - public final Value get(Value key) { - return charValueAt(key.toLong()); - } - - /** - * Sets the array ref. - */ - @Override - public Value put(Value index, Value value) { - setCharValueAt(index.toLong(), value); - - return value; - } - - /** - * Sets the array ref. - */ - @Override - public Value append(Value index, Value value) { - if (_length > 0) { - return setCharValueAt(index.toLong(), value); - } else { - return new ArrayValueImpl().append(index, value); - } - } - - /** - * Returns the buffer. - */ - public final char[] getBuffer() { - return _buffer; - } - - /** - * Returns the offset. - */ - public int getOffset() { - return _length; - } - - /** - * Sets the offset. - */ - public void setOffset(int offset) { - _length = offset; - } - - /** - * Returns the current capacity. - */ - public int getLength() { - return _buffer.length; - } - - /** - * Converts to a BinaryValue. - */ - @Override - public StringValue toBinaryValue() { - return toBinaryValue(Env.getInstance()); - } - - /** - * Converts to a BinaryValue. - */ - @Override - public StringValue toBinaryValue(Env env) { - return toBinaryValue(env.getRuntimeEncoding()); - } - - /** - * Converts to a BinaryValue in desired charset. - * - * @param env - * @param charset - */ - @Override - public StringValue toBinaryValue(String charset) { - try { - BinaryBuilderValue result = new BinaryBuilderValue(); - BinaryBuilderStream stream = new BinaryBuilderStream(result); - - // TODO: can use EncodingWriter directly(?) - WriteStream out = new WriteStream(stream); - out.setEncoding(charset); - - out.print(_buffer, 0, _length); - - out.close(); - - return result; - } catch (IOException e) { - throw new QuercusModuleException(e.getMessage()); - } - } - - /** - * Returns the character at an index - */ - @Override - public Value charValueAt(long index) { - int len = _length; - - if (index < 0 || len <= index) { - return UnsetUnicodeValue.UNSET; - } else { - int ch = _buffer[(int) index]; - - if (ch < CHAR_STRINGS.length) { - return CHAR_STRINGS[ch]; - } else { - return new UnicodeBuilderValue((char) ch); - } - } - } - - /** - * sets the character at an index - */ - @Override - public Value setCharValueAt(long indexL, Value value) { - int len = _length; - - if (indexL < 0) { - return this; - } else if (indexL < len) { - UnicodeBuilderValue sb = new UnicodeBuilderValue(_buffer, 0, len); - - StringValue str = value.toStringValue(); - - int index = (int) indexL; - - if (value.length() == 0) { - sb._buffer[index] = 0; - } else { - sb._buffer[index] = str.charAt(0); - } - - return sb; - } else { - int index = (int) indexL; - - UnicodeBuilderValue sb = (UnicodeBuilderValue) copyStringBuilder(); - - if (sb._buffer.length < index + 1) { - sb.ensureCapacity(index + 1); - } - - int padLen = index - len; - - for (int i = 0; i <= padLen; i++) { - sb._buffer[sb._length++] = ' '; - } - - StringValue str = value.toStringValue(); - - if (value.length() == 0) { - sb._buffer[index] = 0; - } else { - sb._buffer[index] = str.charAt(0); - } - - return sb; - } - } - - // - // CharSequence - // - /** - * Returns the length of the string. - */ - @Override - public int length() { - return _length; - } - - /** - * Returns the character at a particular location - */ - @Override - public char charAt(int index) { - return _buffer[index]; - } - - /** - * Returns a subsequence - */ - @Override - public CharSequence subSequence(int start, int end) { - int len = end - start; - - if (len == 0) { - return EMPTY; - } - - UnicodeBuilderValue sb = new UnicodeBuilderValue(len); - - sb.append(_buffer, start, len); - - return sb; - } - - // - // java.lang.String - // - /** - * Convert to lower case. - */ - @Override - public StringValue toLowerCase() { - int length = _length; - - UnicodeBuilderValue string = new UnicodeBuilderValue(length); - - char[] srcBuffer = _buffer; - char[] dstBuffer = string._buffer; - - for (int i = 0; i < length; i++) { - char ch = srcBuffer[i]; - - if ('A' <= ch && ch <= 'Z') { - dstBuffer[i] = (char) (ch + 'a' - 'A'); - } else if (ch < 0x80) { - dstBuffer[i] = ch; - } else if (Character.isUpperCase(ch)) { - dstBuffer[i] = Character.toLowerCase(ch); - } else { - dstBuffer[i] = ch; - } - } - - string._length = length; - - return string; - } - - /** - * Convert to lower case. - */ - @Override - public StringValue toUpperCase() { - int length = _length; - - UnicodeBuilderValue string = new UnicodeBuilderValue(_length); - - char[] srcBuffer = _buffer; - char[] dstBuffer = string._buffer; - - for (int i = 0; i < length; i++) { - char ch = srcBuffer[i]; - - if ('a' <= ch && ch <= 'z') { - dstBuffer[i] = (char) (ch + 'A' - 'a'); - } else if (ch < 0x80) { - dstBuffer[i] = ch; - } else if (Character.isLowerCase(ch)) { - dstBuffer[i] = Character.toUpperCase(ch); - } else { - dstBuffer[i] = ch; - } - } - - string._length = length; - - return string; - } - - /** - * Returns a character array - */ - @Override - public char[] toCharArray() { - char[] dest = new char[_length]; - - System.arraycopy(_buffer, 0, dest, 0, _length); - - return dest; - } - - /** - * Return the underlying buffer. - */ - @Override - public char[] getRawCharArray() { - return _buffer; - } - - /** - * Prints the value. - * @param env - */ - @Override - public void print(Env env) { - env.print(_buffer, 0, _length); - } - - /** - * Serializes the value. - */ - @Override - public void serialize(Env env, StringBuilder sb) { - sb.append("U:"); - sb.append(_length); - sb.append(":\""); - sb.append(_buffer, 0, _length); - sb.append("\";"); - } - - // - // append code - // - /** - * Creates a string builder of the same type. - */ - @Override - public StringValue createStringBuilder() { - return new UnicodeBuilderValue(); - } - - /** - * Creates a string builder of the same type. - */ - @Override - public StringValue createStringBuilder(int length) { - return new UnicodeBuilderValue(length); - } - - // - // static helper functions - // - public static int getNumericType(char[] buffer, int offset, int len) { - if (len == 0) { - return IS_STRING; - } - - int i = offset; - int ch = 0; - - if (i < len && ((ch = buffer[i]) == '+' || ch == '-')) { - i++; - } - - if (len <= i) { - return IS_STRING; - } - - ch = buffer[i]; - - if (ch == '.') { - for (i++; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { - return IS_DOUBLE; - } - - return IS_STRING; - } else if (!('0' <= ch && ch <= '9')) { - return IS_STRING; - } - - for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { - } - - if (len <= i) { - return IS_LONG; - } else if (ch == '.' || ch == 'e' || ch == 'E') { - for (i++; - i < len && ('0' <= (ch = buffer[i]) && ch <= '9' - || ch == '+' || ch == '-' || ch == 'e' || ch == 'E'); - i++) { - } - - if (i < len) { - return IS_STRING; - } else { - return IS_DOUBLE; - } - } else { - return IS_STRING; - } - } - - public static ValueType getValueType(char[] buffer, int offset, int len) { - if (len == 0) { - // php/0307 - return ValueType.LONG_ADD; - } - - int i = offset; - int ch = 0; - - while (i < len && Character.isWhitespace(buffer[i])) { - i++; - } - - if (i < len && ((ch = buffer[i]) == '+' || ch == '-')) { - i++; - } - - if (len <= i) { - return ValueType.STRING; - } - - ch = buffer[i]; - - if (ch == '.') { - for (i++; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { - return ValueType.DOUBLE_CMP; - } - - return ValueType.STRING; - } else if (!('0' <= ch && ch <= '9')) { - return ValueType.STRING; - } - - for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { - } - - while (i < len && Character.isWhitespace(buffer[i])) { - i++; - } - - if (len <= i) { - return ValueType.LONG_EQ; - } else if (ch == '.' || ch == 'e' || ch == 'E') { - for (i++; - i < len - && ('0' <= (ch = buffer[i]) - && ch <= '9' - || ch == '+' - || ch == '-' - || ch == 'e' - || ch == 'E'); - i++) { - } - - while (i < len && Character.isWhitespace(buffer[i])) { + } + + int sign = 1; + long value = 0; + + int i = 0; + int ch = buffer[i]; + if (ch == '-') { + sign = -1; i++; - } + } - if (i < len) { - return ValueType.STRING; - } else { - return ValueType.DOUBLE_CMP; - } - } else { - return ValueType.STRING; - } - } - - /** - * Converts to a boolean. - */ - @Override - public final boolean toBoolean() { - if (_length == 0) { - return false; - } else if (_length == 1 && _buffer[0] == '0') { - return false; - } else { - return true; - } - } - - /** - * Converts to a long. - */ - @Override - public long toLong() { - return parseLong(_buffer, 0, _length); - } - - /** - * Converts to a long. - */ - public static long toLong(char[] buffer, int offset, int len) { - return parseLong(buffer, offset, len); - } - - /** - * Converts to a double. - */ - @Override - public double toDouble() { - return toDouble(_buffer, 0, _length); - } - - public static double toDouble(char[] buffer, int offset, int len) { - int start = offset; - int i = offset; - int ch = 0; - - while (i < len && Character.isWhitespace(buffer[i])) { - start++; - i++; - } - - int end = len + offset; - - if (offset + 1 < end && buffer[offset] == '0' - && ((ch = buffer[offset + 1]) == 'x' || ch == 'X')) { - - double value = 0; - - for (offset += 2; offset < end; offset++) { - ch = buffer[offset] & 0xFF; + for (; i < len; i++) { + ch = buffer[i]; if ('0' <= ch && ch <= '9') { - value = value * 16 + ch - '0'; - } else if ('a' <= ch && ch <= 'z') { - value = value * 16 + ch - 'a' + 10; - } else if ('A' <= ch && ch <= 'Z') { - value = value * 16 + ch - 'A' + 10; + value = 10 * value + ch - '0'; + } else { + return this; + } + } + + return LongValue.create(sign * value); + } + + /** + * Converts to a byte array, with no consideration of character encoding. + * Each character becomes one byte, characters with values above 255 are + * not correctly preserved. + */ + @Override + public final byte[] toBytes() { + byte[] bytes = new byte[_length]; + + for (int i = 0; i < _length; i++) { + bytes[i] = (byte) (_buffer[i] & 0xFF); + } + + return bytes; + } + + // + // Operations + // + /** + * Returns the character at an index + */ + @Override + public final Value get(Value key) { + return charValueAt(key.toLong()); + } + + /** + * Sets the array ref. + */ + @Override + public Value put(Value index, Value value) { + setCharValueAt(index.toLong(), value); + + return value; + } + + /** + * Sets the array ref. + */ + @Override + public Value append(Value index, Value value) { + if (_length > 0) { + return setCharValueAt(index.toLong(), value); + } else { + return new ArrayValueImpl().append(index, value); + } + } + + /** + * Returns the buffer. + */ + public final char[] getBuffer() { + return _buffer; + } + + /** + * Returns the offset. + */ + public int getOffset() { + return _length; + } + + /** + * Sets the offset. + */ + public void setOffset(int offset) { + _length = offset; + } + + /** + * Returns the current capacity. + */ + public int getLength() { + return _buffer.length; + } + + /** + * Converts to a BinaryValue. + */ + @Override + public StringValue toBinaryValue() { + return toBinaryValue(Env.getInstance()); + } + + /** + * Converts to a BinaryValue. + */ + @Override + public StringValue toBinaryValue(Env env) { + return toBinaryValue(env.getRuntimeEncoding()); + } + + /** + * Converts to a BinaryValue in desired charset. + * + * @param env + * @param charset + */ + @Override + public StringValue toBinaryValue(String charset) { + try { + BinaryBuilderValue result = new BinaryBuilderValue(); + BinaryBuilderStream stream = new BinaryBuilderStream(result); + + // TODO: can use EncodingWriter directly(?) + WriteStream out = new WriteStream(stream); + out.setEncoding(charset); + + out.print(_buffer, 0, _length); + + out.close(); + + return result; + } catch (IOException e) { + throw new QuercusModuleException(e.getMessage()); + } + } + + /** + * Returns the character at an index + */ + @Override + public Value charValueAt(long index) { + int len = _length; + + if (index < 0 || len <= index) { + return UnsetUnicodeValue.UNSET; + } else { + int ch = _buffer[(int) index]; + + if (ch < CHAR_STRINGS.length) { + return CHAR_STRINGS[ch]; } else { - return value; + return new UnicodeBuilderValue((char) ch); } - } + } + } - return value; - } + /** + * sets the character at an index + */ + @Override + public Value setCharValueAt(long indexL, Value value) { + int len = _length; - if (i < len && ((ch = buffer[i]) == '+' || ch == '-')) { - i++; - } + if (indexL < 0) { + return this; + } else if (indexL < len) { + UnicodeBuilderValue sb = new UnicodeBuilderValue(_buffer, 0, len); - for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { - } + StringValue str = value.toStringValue(); - if (ch == '.') { - for (i++; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { - } + int index = (int) indexL; - if (i == 1) { - return 0; - } - } + if (value.length() == 0) { + sb._buffer[index] = 0; + } else { + sb._buffer[index] = str.charAt(0); + } + + return sb; + } else { + int index = (int) indexL; + + UnicodeBuilderValue sb = (UnicodeBuilderValue) copyStringBuilder(); + + if (sb._buffer.length < index + 1) { + sb.ensureCapacity(index + 1); + } + + int padLen = index - len; + + for (int i = 0; i <= padLen; i++) { + sb._buffer[sb._length++] = ' '; + } + + StringValue str = value.toStringValue(); + + if (value.length() == 0) { + sb._buffer[index] = 0; + } else { + sb._buffer[index] = str.charAt(0); + } + + return sb; + } + } + + // + // CharSequence + // + /** + * Returns the length of the string. + */ + @Override + public int length() { + return _length; + } + + /** + * Returns the character at a particular location + */ + @Override + public char charAt(int index) { + return _buffer[index]; + } + + /** + * Returns a subsequence + */ + @Override + public CharSequence subSequence(int start, int end) { + int len = end - start; + + if (len == 0) { + return EMPTY; + } + + UnicodeBuilderValue sb = new UnicodeBuilderValue(len); + + sb.append(_buffer, start, len); + + return sb; + } + + // + // java.lang.String + // + /** + * Convert to lower case. + */ + @Override + public StringValue toLowerCase() { + int length = _length; + + UnicodeBuilderValue string = new UnicodeBuilderValue(length); + + char[] srcBuffer = _buffer; + char[] dstBuffer = string._buffer; + + for (int i = 0; i < length; i++) { + char ch = srcBuffer[i]; + + if ('A' <= ch && ch <= 'Z') { + dstBuffer[i] = (char) (ch + 'a' - 'A'); + } else if (ch < 0x80) { + dstBuffer[i] = ch; + } else if (Character.isUpperCase(ch)) { + dstBuffer[i] = Character.toLowerCase(ch); + } else { + dstBuffer[i] = ch; + } + } - if (ch == 'e' || ch == 'E') { - int e = i++; + string._length = length; - if (i < len && (ch = buffer[i]) == '+' || ch == '-') { + return string; + } + + /** + * Convert to lower case. + */ + @Override + public StringValue toUpperCase() { + int length = _length; + + UnicodeBuilderValue string = new UnicodeBuilderValue(_length); + + char[] srcBuffer = _buffer; + char[] dstBuffer = string._buffer; + + for (int i = 0; i < length; i++) { + char ch = srcBuffer[i]; + + if ('a' <= ch && ch <= 'z') { + dstBuffer[i] = (char) (ch + 'A' - 'a'); + } else if (ch < 0x80) { + dstBuffer[i] = ch; + } else if (Character.isLowerCase(ch)) { + dstBuffer[i] = Character.toUpperCase(ch); + } else { + dstBuffer[i] = ch; + } + } + + string._length = length; + + return string; + } + + /** + * Returns a character array + */ + @Override + public char[] toCharArray() { + char[] dest = new char[_length]; + + System.arraycopy(_buffer, 0, dest, 0, _length); + + return dest; + } + + /** + * Return the underlying buffer. + */ + @Override + public char[] getRawCharArray() { + return _buffer; + } + + /** + * Prints the value. + * @param env + */ + @Override + public void print(Env env) { + env.print(_buffer, 0, _length); + } + + /** + * Serializes the value. + */ + @Override + public void serialize(Env env, StringBuilder sb) { + sb.append("U:"); + sb.append(_length); + sb.append(":\""); + sb.append(_buffer, 0, _length); + sb.append("\";"); + } + + // + // append code + // + /** + * Creates a string builder of the same type. + */ + @Override + public StringValue createStringBuilder() { + return new UnicodeBuilderValue(); + } + + /** + * Creates a string builder of the same type. + */ + @Override + public StringValue createStringBuilder(int length) { + return new UnicodeBuilderValue(length); + } + + // + // static helper functions + // + public static int getNumericType(char[] buffer, int offset, int len) { + if (len == 0) { + return IS_STRING; + } + + int i = offset; + int ch = 0; + + if (i < len && ((ch = buffer[i]) == '+' || ch == '-')) { i++; - } - - for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { - } - - if (i == e + 1) { - i = e; - } - } - - if (i == 0) { - return 0; - } - - try { - return Double.parseDouble(new String(buffer, start, i - start)); - } catch (NumberFormatException e) { - return 0; - } - } - - @Override - public void ensureAppendCapacity(int newCapacity) { - ensureCapacity(_length + newCapacity); - } - - protected void ensureCapacity(int newCapacity) { - int bufferLength = _buffer.length; - - if (newCapacity <= bufferLength) { - return; - } - - if (bufferLength < MIN_LENGTH) { - bufferLength = MIN_LENGTH; - } - - while (bufferLength <= newCapacity) { - bufferLength = 2 * bufferLength; - } - - char[] buffer = new char[bufferLength]; - System.arraycopy(_buffer, 0, buffer, 0, _length); - _buffer = buffer; - _isCopy = false; - } - - /** - * Returns the hash code. - */ - @Override - public int hashCode() { - int hash = _hashCode; - - if (hash != 0) { - return hash; - } - - hash = 37; - - int length = _length; - char[] buffer = _buffer; - - if (length > 256) { - for (int i = 127; i >= 0; i--) { - hash = 65521 * hash + buffer[i]; - } + } - for (int i = length - 128; i < length; i++) { - hash = 65521 * hash + buffer[i]; - } + if (len <= i) { + return IS_STRING; + } - _hashCode = hash; + ch = buffer[i]; - return hash; - } + if (ch == '.') { + for (i++; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { + return IS_DOUBLE; + } - for (int i = length - 1; i >= 0; i--) { - hash = 65521 * hash + buffer[i]; - } + return IS_STRING; + } else if (!('0' <= ch && ch <= '9')) { + return IS_STRING; + } + + for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { + } + + if (len <= i) { + return IS_LONG; + } else if (ch == '.' || ch == 'e' || ch == 'E') { + for (i++; + i < len && ('0' <= (ch = buffer[i]) && ch <= '9' + || ch == '+' || ch == '-' || ch == 'e' || ch == 'E'); + i++) { + } - _hashCode = hash; + if (i < len) { + return IS_STRING; + } else { + return IS_DOUBLE; + } + } else { + return IS_STRING; + } + } - return hash; - } + public static ValueType getValueType(char[] buffer, int offset, int len) { + if (len == 0) { + // php/0307 + return ValueType.LONG_ADD; + } - /** - * Returns true for equality - */ - @Override - public boolean eq(Value rValue) { - rValue = rValue.toValue(); + int i = offset; + int ch = 0; - ValueType typeB = rValue.getValueType(); + while (i < len && Character.isWhitespace(buffer[i])) { + i++; + } - if (typeB.isNumber()) { - double l = toDouble(); - double r = rValue.toDouble(); + if (i < len && ((ch = buffer[i]) == '+' || ch == '-')) { + i++; + } - return l == r; - } else if (typeB.isBoolean()) { - return toBoolean() == rValue.toBoolean(); - } + if (len <= i) { + return ValueType.STRING; + } - ValueType typeA = getValueType(); - if (typeA.isNumberCmp() && typeB.isNumberCmp()) { - double l = toDouble(); - double r = rValue.toDouble(); + ch = buffer[i]; - return l == r; - } + if (ch == '.') { + for (i++; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { + return ValueType.DOUBLE_CMP; + } - if (rValue instanceof UnicodeBuilderValue) { - UnicodeBuilderValue value = (UnicodeBuilderValue) rValue; + return ValueType.STRING; + } else if (!('0' <= ch && ch <= '9')) { + return ValueType.STRING; + } - int length = _length; + for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { + } - if (length != value._length) { + while (i < len && Character.isWhitespace(buffer[i])) { + i++; + } + + if (len <= i) { + return ValueType.LONG_EQ; + } else if (ch == '.' || ch == 'e' || ch == 'E') { + for (i++; + i < len + && ('0' <= (ch = buffer[i]) + && ch <= '9' + || ch == '+' + || ch == '-' + || ch == 'e' + || ch == 'E'); + i++) { + } + + while (i < len && Character.isWhitespace(buffer[i])) { + i++; + } + + if (i < len) { + return ValueType.STRING; + } else { + return ValueType.DOUBLE_CMP; + } + } else { + return ValueType.STRING; + } + } + + /** + * Converts to a boolean. + */ + @Override + public final boolean toBoolean() { + if (_length == 0) { + return false; + } else if (_length == 1 && _buffer[0] == '0') { return false; - } + } else { + return true; + } + } + + /** + * Converts to a long. + */ + @Override + public long toLong() { + return parseLong(_buffer, 0, _length); + } + + /** + * Converts to a long. + */ + public static long toLong(char[] buffer, int offset, int len) { + return parseLong(buffer, offset, len); + } + + /** + * Converts to a double. + */ + @Override + public double toDouble() { + return toDouble(_buffer, 0, _length); + } + + public static double toDouble(char[] buffer, int offset, int len) { + int start = offset; + int i = offset; + int ch = 0; + + while (i < len && Character.isWhitespace(buffer[i])) { + start++; + i++; + } + + int end = len + offset; + + if (offset + 1 < end && buffer[offset] == '0' + && ((ch = buffer[offset + 1]) == 'x' || ch == 'X')) { + + double value = 0; - char[] bufferA = _buffer; - char[] bufferB = value._buffer; + for (offset += 2; offset < end; offset++) { + ch = buffer[offset] & 0xFF; - for (int i = length - 1; i >= 0; i--) { - if (bufferA[i] != bufferB[i]) { - return false; + if ('0' <= ch && ch <= '9') { + value = value * 16 + ch - '0'; + } else if ('a' <= ch && ch <= 'z') { + value = value * 16 + ch - 'a' + 10; + } else if ('A' <= ch && ch <= 'Z') { + value = value * 16 + ch - 'A' + 10; + } else { + return value; + } } - } - return true; - } else { - String rString = rValue.toString(); + return value; + } - int len = rString.length(); + if (i < len && ((ch = buffer[i]) == '+' || ch == '-')) { + i++; + } - if (_length != len) { - return false; - } + for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { + } + + if (ch == '.') { + for (i++; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { + } - for (int i = len - 1; i >= 0; i--) { - if (_buffer[i] != rString.charAt(i)) { - return false; + if (i == 1) { + return 0; } - } + } - return true; - } - } + if (ch == 'e' || ch == 'E') { + int e = i++; - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } + if (i < len && (ch = buffer[i]) == '+' || ch == '-') { + i++; + } - if (o instanceof UnicodeBuilderValue) { - UnicodeBuilderValue value = (UnicodeBuilderValue) o; + for (; i < len && '0' <= (ch = buffer[i]) && ch <= '9'; i++) { + } - int length = _length; + if (i == e + 1) { + i = e; + } + } - if (length != value._length) { - return false; - } + if (i == 0) { + return 0; + } + + try { + return Double.parseDouble(new String(buffer, start, i - start)); + } catch (NumberFormatException e) { + return 0; + } + } + + @Override + public void ensureAppendCapacity(int newCapacity) { + ensureCapacity(_length + newCapacity); + } + + protected void ensureCapacity(int newCapacity) { + int bufferLength = _buffer.length; + + if (newCapacity <= bufferLength) { + return; + } + + if (bufferLength < MIN_LENGTH) { + bufferLength = MIN_LENGTH; + } + + while (bufferLength <= newCapacity) { + bufferLength = 2 * bufferLength; + } + + char[] buffer = new char[bufferLength]; + System.arraycopy(_buffer, 0, buffer, 0, _length); + _buffer = buffer; + _isCopy = false; + } + + /** + * Returns the hash code. + */ + @Override + public int hashCode() { + int hash = _hashCode; + + if (hash != 0) { + //System.err.println("UnicodeBV Hash code for 1 " + this + " = " + hash); + return hash; + } + + hash = 37; + + int length = _length; + char[] buffer = _buffer; + + if (length > 256) { + for (int i = 127; i >= 0; i--) { + hash = 65521 * hash + buffer[i]; + } + + for (int i = length - 128; i < length; i++) { + hash = 65521 * hash + buffer[i]; + } + + _hashCode = hash; + //System.err.println("UnicodeBV Hash code for 2 " + this + " = " + hash); + return hash; + } + + for (int i = length - 1; i >= 0; i--) { + hash = 65521 * hash + buffer[i]; + } - char[] bufferA = _buffer; - char[] bufferB = value._buffer; + _hashCode = hash; + //System.err.println("UnicodeBV Hash code for 3" + this + " = " + hash); + return hash; + } - for (int i = length - 1; i >= 0; i--) { - if (bufferA[i] != bufferB[i]) { - return false; + /** + * Returns true for equality + */ + @Override + public boolean eq(Value rValue) { + rValue = rValue.toValue(); + + ValueType typeB = rValue.getValueType(); + + if (typeB.isNumber()) { + double l = toDouble(); + double r = rValue.toDouble(); + + return l == r; + } else if (typeB.isBoolean()) { + return toBoolean() == rValue.toBoolean(); + } + + ValueType typeA = getValueType(); + if (typeA.isNumberCmp() && typeB.isNumberCmp()) { + double l = toDouble(); + double r = rValue.toDouble(); + + return l == r; + } + + if (rValue instanceof UnicodeBuilderValue) { + UnicodeBuilderValue value = (UnicodeBuilderValue) rValue; + + int length = _length; + + if (length != value._length) { + return false; + } + + char[] bufferA = _buffer; + char[] bufferB = value._buffer; + + for (int i = length - 1; i >= 0; i--) { + if (bufferA[i] != bufferB[i]) { + return false; + } + } + + return true; + } else { + String rString = rValue.toString(); + + int len = rString.length(); + + if (_length != len) { + return false; + } + + for (int i = len - 1; i >= 0; i--) { + if (_buffer[i] != rString.charAt(i)) { + return false; + } } - } - return true; - } /* - else if (o instanceof UnicodeValue) { - UnicodeValue value = (UnicodeValue)o; + return true; + } + } - return value.equals(this); - } - */ else { - return false; - } - } + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } - @Override - public boolean eql(Value o) { - o = o.toValue(); + if (o instanceof UnicodeBuilderValue) { + UnicodeBuilderValue value = (UnicodeBuilderValue) o; - if (o == this) { - return true; - } + int length = _length; - if (o instanceof UnicodeBuilderValue) { - UnicodeBuilderValue value = (UnicodeBuilderValue) o; + if (length != value._length) { + return false; + } - int length = _length; + char[] bufferA = _buffer; + char[] bufferB = value._buffer; - if (length != value._length) { + for (int i = length - 1; i >= 0; i--) { + if (bufferA[i] != bufferB[i]) { + return false; + } + } + + return true; + } /* + else if (o instanceof UnicodeValue) { + UnicodeValue value = (UnicodeValue)o; + + return value.equals(this); + } + */ else { return false; - } + } + } + + @Override + public boolean eql(Value o) { + o = o.toValue(); - char[] bufferA = _buffer; - char[] bufferB = value._buffer; + if (o == this) { + return true; + } - for (int i = length - 1; i >= 0; i--) { - if (bufferA[i] != bufferB[i]) { - return false; + if (o instanceof UnicodeBuilderValue) { + UnicodeBuilderValue value = (UnicodeBuilderValue) o; + + int length = _length; + + if (length != value._length) { + return false; } - } - - return true; - } else { - return false; - } - } - - private void readObject(ObjectInputStream in) - throws ClassNotFoundException, IOException { - _length = in.readInt(); - _buffer = new char[_length]; - - for (int i = 0; i < _length; i++) { - _buffer[i] = (char) (in.read() & 0xFF); - } - } - - @Override - public String toString() { - return String.valueOf(_buffer, 0, _length); - } - - static { - CHAR_STRINGS = new UnicodeBuilderValue[256]; - - for (int i = 0; i < CHAR_STRINGS.length; i++) { - CHAR_STRINGS[i] = new UnicodeBuilderValue((char) i); - } - } + + char[] bufferA = _buffer; + char[] bufferB = value._buffer; + + for (int i = length - 1; i >= 0; i--) { + if (bufferA[i] != bufferB[i]) { + return false; + } + } + + return true; + } else { + return false; + } + } + + private void readObject(ObjectInputStream in) + throws ClassNotFoundException, IOException { + _length = in.readInt(); + _buffer = new char[_length]; + + for (int i = 0; i < _length; i++) { + _buffer[i] = (char) (in.read() & 0xFF); + } + } + + @Override + public String toString() { + return String.valueOf(_buffer, 0, _length); + } + + static { + CHAR_STRINGS = new UnicodeBuilderValue[256]; + + for (int i = 0; i < CHAR_STRINGS.length; i++) { + CHAR_STRINGS[i] = new UnicodeBuilderValue((char) i); + } + } } diff --git a/quercus/src/main/java/com/caucho/quercus/env/Value.java b/quercus/src/main/java/com/caucho/quercus/env/Value.java index 007d944..9f3c02b 100644 --- a/quercus/src/main/java/com/caucho/quercus/env/Value.java +++ b/quercus/src/main/java/com/caucho/quercus/env/Value.java @@ -49,2651 +49,2651 @@ */ abstract public class Value implements java.io.Serializable { - protected static final L10N L = new L10N(Value.class); - private static final Value[] NULL_ARG_VALUES = new Value[0]; - public static final StringValue SCALAR_V = new ConstStringValue("scalar"); - public static final Value[] NULL_VALUE_ARRAY = new Value[0]; - public static final Value[] NULL_ARGS = new Value[0]; - - // - // Properties - // - /** - * Returns the value's class name. - */ - public String getClassName() { - return getType(); - } - - /** - * Returns the backing QuercusClass. - */ - public QuercusClass getQuercusClass() { - return null; - } - - /** - * Returns the called class - */ - public Value getCalledClass(Env env) { - QuercusClass qClass = getQuercusClass(); - - if (qClass != null) { - return env.createString(qClass.getName()); - } else { - env.warning(L.l("get_called_class() must be called in a class context")); - - return BooleanValue.FALSE; - } - } - - // - // Predicates and Relations - // - /** - * Returns true for an implementation of a class - */ - public boolean isA(String name) { - return false; - } - - /** - * Returns true for an implementation of a class - */ - final public boolean isA(Value value) { - if (value.isObject()) { - return isA(value.getClassName()); - } else { - return isA(value.toString()); - } - } - - /** - * Checks if 'this' is a valid protected call for 'className' - */ - public void checkProtected(Env env, String className) { - } - - /** - * Checks if 'this' is a valid private call for 'className' - */ - public void checkPrivate(Env env, String className) { - } - - /** - * Returns the ValueType. - */ - public ValueType getValueType() { - return ValueType.VALUE; - } - - /** - * Returns true for an array. - */ - public boolean isArray() { - return false; - } - - /** - * Returns true for a double-value. - */ - public boolean isDoubleConvertible() { - return false; - } - - /** - * Returns true for a long-value. - */ - public boolean isLongConvertible() { - return false; - } - - /** - * Returns true for a long-value. - */ - public boolean isLong() { - return false; - } - - /** - * Returns true for a long-value. - */ - public boolean isDouble() { - return false; - } - - /** - * Returns true for a null. - */ - public boolean isNull() { - return false; - } - - /** - * Returns true for a number. - */ - public boolean isNumberConvertible() { - return isLongConvertible() || isDoubleConvertible(); - } - - /** - * Matches is_numeric - */ - public boolean isNumeric() { - return false; - } - - /** - * Returns true for an object. - */ - public boolean isObject() { - return false; - } - - /* - * Returns true for a resource. - */ - public boolean isResource() { - return false; - } - - /** - * Returns true for a StringValue. - */ - public boolean isString() { - return false; - } - - /** - * Returns true for a BinaryValue. - */ - public boolean isBinary() { - return false; - } - - /** - * Returns true for a UnicodeValue. - */ - public boolean isUnicode() { - return false; - } - - /** - * Returns true for a BooleanValue - */ - public boolean isBoolean() { - return false; - } - - /** - * Returns true for a DefaultValue - */ - public boolean isDefault() { - return false; - } - - // - // marshal costs - // - /** - * Cost to convert to a boolean - */ - public int toBooleanMarshalCost() { - return Marshal.COST_TO_BOOLEAN; - } - - /** - * Cost to convert to a byte - */ - public int toByteMarshalCost() { - return Marshal.COST_INCOMPATIBLE; - } - - /** - * Cost to convert to a short - */ - public int toShortMarshalCost() { - return Marshal.COST_INCOMPATIBLE; - } - - /** - * Cost to convert to an integer - */ - public int toIntegerMarshalCost() { - return Marshal.COST_INCOMPATIBLE; - } - - /** - * Cost to convert to a long - */ - public int toLongMarshalCost() { - return Marshal.COST_INCOMPATIBLE; - } - - /** - * Cost to convert to a double - */ - public int toDoubleMarshalCost() { - return Marshal.COST_INCOMPATIBLE; - } - - /** - * Cost to convert to a float - */ - public int toFloatMarshalCost() { - return toDoubleMarshalCost() + 10; - } - - /** - * Cost to convert to a character - */ - public int toCharMarshalCost() { - return Marshal.COST_TO_CHAR; - } - - /** - * Cost to convert to a string - */ - public int toStringMarshalCost() { - return Marshal.COST_TO_STRING; - } - - /** - * Cost to convert to a byte[] - */ - public int toByteArrayMarshalCost() { - return Marshal.COST_TO_BYTE_ARRAY; - } - - /** - * Cost to convert to a char[] - */ - public int toCharArrayMarshalCost() { - return Marshal.COST_TO_CHAR_ARRAY; - } - - /** - * Cost to convert to a Java object - */ - public int toJavaObjectMarshalCost() { - return Marshal.COST_TO_JAVA_OBJECT; - } - - /** - * Cost to convert to a binary value - */ - public int toBinaryValueMarshalCost() { - return Marshal.COST_TO_STRING + 1; - } - - /** - * Cost to convert to a StringValue - */ - public int toStringValueMarshalCost() { - return Marshal.COST_TO_STRING + 1; - } - - /** - * Cost to convert to a UnicodeValue - */ - public int toUnicodeValueMarshalCost() { - return Marshal.COST_TO_STRING + 1; - } - - // - // predicates - // - /** - * Returns true if the value is set. - */ - public boolean isset() { - return true; - } - - /** - * Returns true if the value is empty - */ - public boolean isEmpty() { - return false; - } - - /** - * Returns true if there are more elements. - */ - public boolean hasCurrent() { - return false; - } - - /** - * Returns true for equality - */ - public Value eqValue(Value rValue) { - return eq(rValue) ? BooleanValue.TRUE : BooleanValue.FALSE; - } - - /** - * Returns true for equality - */ - public boolean eq(Value rValue) { - if (rValue.isArray()) { - return rValue.eq(this); - } else if (rValue instanceof BooleanValue) { - return toBoolean() == rValue.toBoolean(); - } else if (isLongConvertible() && rValue.isLongConvertible()) { - return toLong() == rValue.toLong(); - } else if (isNumberConvertible() || rValue.isNumberConvertible()) { - return toDouble() == rValue.toDouble(); - } else { - return toString().equals(rValue.toString()); - } - } - - /** - * Returns true for equality - */ - public boolean eql(Value rValue) { - return this == rValue.toValue(); - } - - /** - * Returns a negative/positive integer if this Value is - * lessthan/greaterthan rValue. - */ - public int cmp(Value rValue) { - // This is tricky: implemented according to Table 15-5 of - // http://us2.php.net/manual/en/language.operators.comparison.php - - Value lVal = toValue(); - Value rVal = rValue.toValue(); - - if (lVal instanceof StringValue && rVal instanceof NullValue) { - return ((StringValue) lVal).cmpString(StringValue.EMPTY); - } - - if (lVal instanceof NullValue && rVal instanceof StringValue) { - return StringValue.EMPTY.cmpString((StringValue) rVal); - } - - if (lVal instanceof StringValue && rVal instanceof StringValue) { - return ((StringValue) lVal).cmpString((StringValue) rVal); - } - - if (lVal instanceof NullValue - || lVal instanceof BooleanValue - || rVal instanceof NullValue - || rVal instanceof BooleanValue) { - boolean lBool = toBoolean(); - boolean rBool = rValue.toBoolean(); - - if (!lBool && rBool) { + protected static final L10N L = new L10N(Value.class); + private static final Value[] NULL_ARG_VALUES = new Value[0]; + public static final StringValue SCALAR_V = new ConstStringValue("scalar"); + public static final Value[] NULL_VALUE_ARRAY = new Value[0]; + public static final Value[] NULL_ARGS = new Value[0]; + + // + // Properties + // + /** + * Returns the value's class name. + */ + public String getClassName() { + return getType(); + } + + /** + * Returns the backing QuercusClass. + */ + public QuercusClass getQuercusClass() { + return null; + } + + /** + * Returns the called class + */ + public Value getCalledClass(Env env) { + QuercusClass qClass = getQuercusClass(); + + if (qClass != null) { + return env.createString(qClass.getName()); + } else { + env.warning(L.l("get_called_class() must be called in a class context")); + + return BooleanValue.FALSE; + } + } + + // + // Predicates and Relations + // + /** + * Returns true for an implementation of a class + */ + public boolean isA(String name) { + return false; + } + + /** + * Returns true for an implementation of a class + */ + final public boolean isA(Value value) { + if (value.isObject()) { + return isA(value.getClassName()); + } else { + return isA(value.toString()); + } + } + + /** + * Checks if 'this' is a valid protected call for 'className' + */ + public void checkProtected(Env env, String className) { + } + + /** + * Checks if 'this' is a valid private call for 'className' + */ + public void checkPrivate(Env env, String className) { + } + + /** + * Returns the ValueType. + */ + public ValueType getValueType() { + return ValueType.VALUE; + } + + /** + * Returns true for an array. + */ + public boolean isArray() { + return false; + } + + /** + * Returns true for a double-value. + */ + public boolean isDoubleConvertible() { + return false; + } + + /** + * Returns true for a long-value. + */ + public boolean isLongConvertible() { + return false; + } + + /** + * Returns true for a long-value. + */ + public boolean isLong() { + return false; + } + + /** + * Returns true for a long-value. + */ + public boolean isDouble() { + return false; + } + + /** + * Returns true for a null. + */ + public boolean isNull() { + return false; + } + + /** + * Returns true for a number. + */ + public boolean isNumberConvertible() { + return isLongConvertible() || isDoubleConvertible(); + } + + /** + * Matches is_numeric + */ + public boolean isNumeric() { + return false; + } + + /** + * Returns true for an object. + */ + public boolean isObject() { + return false; + } + + /* + * Returns true for a resource. + */ + public boolean isResource() { + return false; + } + + /** + * Returns true for a StringValue. + */ + public boolean isString() { + return false; + } + + /** + * Returns true for a BinaryValue. + */ + public boolean isBinary() { + return false; + } + + /** + * Returns true for a UnicodeValue. + */ + public boolean isUnicode() { + return false; + } + + /** + * Returns true for a BooleanValue + */ + public boolean isBoolean() { + return false; + } + + /** + * Returns true for a DefaultValue + */ + public boolean isDefault() { + return false; + } + + // + // marshal costs + // + /** + * Cost to convert to a boolean + */ + public int toBooleanMarshalCost() { + return Marshal.COST_TO_BOOLEAN; + } + + /** + * Cost to convert to a byte + */ + public int toByteMarshalCost() { + return Marshal.COST_INCOMPATIBLE; + } + + /** + * Cost to convert to a short + */ + public int toShortMarshalCost() { + return Marshal.COST_INCOMPATIBLE; + } + + /** + * Cost to convert to an integer + */ + public int toIntegerMarshalCost() { + return Marshal.COST_INCOMPATIBLE; + } + + /** + * Cost to convert to a long + */ + public int toLongMarshalCost() { + return Marshal.COST_INCOMPATIBLE; + } + + /** + * Cost to convert to a double + */ + public int toDoubleMarshalCost() { + return Marshal.COST_INCOMPATIBLE; + } + + /** + * Cost to convert to a float + */ + public int toFloatMarshalCost() { + return toDoubleMarshalCost() + 10; + } + + /** + * Cost to convert to a character + */ + public int toCharMarshalCost() { + return Marshal.COST_TO_CHAR; + } + + /** + * Cost to convert to a string + */ + public int toStringMarshalCost() { + return Marshal.COST_TO_STRING; + } + + /** + * Cost to convert to a byte[] + */ + public int toByteArrayMarshalCost() { + return Marshal.COST_TO_BYTE_ARRAY; + } + + /** + * Cost to convert to a char[] + */ + public int toCharArrayMarshalCost() { + return Marshal.COST_TO_CHAR_ARRAY; + } + + /** + * Cost to convert to a Java object + */ + public int toJavaObjectMarshalCost() { + return Marshal.COST_TO_JAVA_OBJECT; + } + + /** + * Cost to convert to a binary value + */ + public int toBinaryValueMarshalCost() { + return Marshal.COST_TO_STRING + 1; + } + + /** + * Cost to convert to a StringValue + */ + public int toStringValueMarshalCost() { + return Marshal.COST_TO_STRING + 1; + } + + /** + * Cost to convert to a UnicodeValue + */ + public int toUnicodeValueMarshalCost() { + return Marshal.COST_TO_STRING + 1; + } + + // + // predicates + // + /** + * Returns true if the value is set. + */ + public boolean isset() { + return true; + } + + /** + * Returns true if the value is empty + */ + public boolean isEmpty() { + return false; + } + + /** + * Returns true if there are more elements. + */ + public boolean hasCurrent() { + return false; + } + + /** + * Returns true for equality + */ + public Value eqValue(Value rValue) { + return eq(rValue) ? BooleanValue.TRUE : BooleanValue.FALSE; + } + + /** + * Returns true for equality + */ + public boolean eq(Value rValue) { + if (rValue.isArray()) { + return rValue.eq(this); + } else if (rValue instanceof BooleanValue) { + return toBoolean() == rValue.toBoolean(); + } else if (isLongConvertible() && rValue.isLongConvertible()) { + return toLong() == rValue.toLong(); + } else if (isNumberConvertible() || rValue.isNumberConvertible()) { + return toDouble() == rValue.toDouble(); + } else { + return toString().equals(rValue.toString()); + } + } + + /** + * Returns true for equality + */ + public boolean eql(Value rValue) { + return this == rValue.toValue(); + } + + /** + * Returns a negative/positive integer if this Value is + * lessthan/greaterthan rValue. + */ + public int cmp(Value rValue) { + // This is tricky: implemented according to Table 15-5 of + // http://us2.php.net/manual/en/language.operators.comparison.php + + Value lVal = toValue(); + Value rVal = rValue.toValue(); + + if (lVal instanceof StringValue && rVal instanceof NullValue) { + return ((StringValue) lVal).cmpString(StringValue.EMPTY); + } + + if (lVal instanceof NullValue && rVal instanceof StringValue) { + return StringValue.EMPTY.cmpString((StringValue) rVal); + } + + if (lVal instanceof StringValue && rVal instanceof StringValue) { + return ((StringValue) lVal).cmpString((StringValue) rVal); + } + + if (lVal instanceof NullValue + || lVal instanceof BooleanValue + || rVal instanceof NullValue + || rVal instanceof BooleanValue) { + boolean lBool = toBoolean(); + boolean rBool = rValue.toBoolean(); + + if (!lBool && rBool) { + return -1; + } + if (lBool && !rBool) { + return 1; + } + return 0; + } + + if (lVal.isObject() && rVal.isObject()) { + return ((ObjectValue) lVal).cmpObject((ObjectValue) rVal); + } + + if ((lVal instanceof StringValue + || lVal instanceof NumberValue + || lVal instanceof ResourceValue) + && (rVal instanceof StringValue + || rVal instanceof NumberValue + || rVal instanceof ResourceValue)) { + return NumberValue.compareNum(lVal, rVal); + } + + if (lVal instanceof ArrayValue) { + return 1; + } + if (rVal instanceof ArrayValue) { return -1; - } - if (lBool && !rBool) { + } + if (lVal instanceof ObjectValue) { return 1; - } - return 0; - } - - if (lVal.isObject() && rVal.isObject()) { - return ((ObjectValue) lVal).cmpObject((ObjectValue) rVal); - } - - if ((lVal instanceof StringValue - || lVal instanceof NumberValue - || lVal instanceof ResourceValue) - && (rVal instanceof StringValue - || rVal instanceof NumberValue - || rVal instanceof ResourceValue)) { - return NumberValue.compareNum(lVal, rVal); - } - - if (lVal instanceof ArrayValue) { - return 1; - } - if (rVal instanceof ArrayValue) { - return -1; - } - if (lVal instanceof ObjectValue) { - return 1; - } - if (rVal instanceof ObjectValue) { - return -1; - } - - // TODO: proper default case? - throw new RuntimeException( - "values are incomparable: " + lVal + " <=> " + rVal); - } - - /** - * Returns true for less than - */ - public boolean lt(Value rValue) { - return cmp(rValue) < 0; - } - - /** - * Returns true for less than or equal to - */ - public boolean leq(Value rValue) { - return cmp(rValue) <= 0; - } - - /** - * Returns true for greater than - */ - public boolean gt(Value rValue) { - return cmp(rValue) > 0; - } - - /** - * Returns true for greater than or equal to - */ - public boolean geq(Value rValue) { - return cmp(rValue) >= 0; - } - - // - // Conversions - // - /** - * Converts to a boolean. - */ - public boolean toBoolean() { - return true; - } - - /** - * Converts to a long. - */ - public long toLong() { - return toBoolean() ? 1 : 0; - } - - /** - * Converts to an int - */ - public int toInt() { - return (int) toLong(); - } - - /** - * Converts to a double. - */ - public double toDouble() { - return 0; - } - - /** - * Converts to a char - */ - public char toChar() { - String s = toString(); - - if (s == null || s.length() < 1) { - return 0; - } else { - return s.charAt(0); - } - } - - /** - * Converts to a string. - * - * @param env - */ - public StringValue toString(Env env) { - return toStringValue(); - } - - /** - * Converts to an array. - */ - public Value toArray() { - return new ArrayValueImpl().append(this); - } - - /** - * Converts to an array if null. - */ - public Value toAutoArray() { - Env.getCurrent().warning(L.l("'{0}' cannot be used as an array.", - toDebugString())); - - return this; - } - - /** - * Casts to an array. - */ - public ArrayValue toArrayValue(Env env) { - env.warning(L.l("'{0}' ({1}) is not assignable to ArrayValue", - this, getType())); - - return null; - } - - /** - * Converts to an object if null. - */ - public Value toAutoObject(Env env) { - return this; - } - - /** - * Converts to an object. - */ - public Value toObject(Env env) { - ObjectValue obj = env.createObject(); - - obj.putField(env, env.createString("scalar"), this); - - return obj; - } - - /** - * Converts to a java object. - */ - public Object toJavaObject() { - return null; - } - - /** - * Converts to a java object. - */ - public Object toJavaObject(Env env, Class type) { - env.warning(L.l("Can't convert {0} to Java {1}", - getClass().getName(), type.getName())); - - return null; - } - - /** - * Converts to a java object. - */ - public Object toJavaObjectNotNull(Env env, Class type) { - env.warning(L.l("Can't convert {0} to Java {1}", - getClass().getName(), type.getName())); - - return null; - } - - /** - * Converts to a java boolean object. - */ - public Boolean toJavaBoolean() { - return toBoolean() ? Boolean.TRUE : Boolean.FALSE; - } - - /** - * Converts to a java byte object. - */ - public Byte toJavaByte() { - return new Byte((byte) toLong()); - } - - /** - * Converts to a java short object. - */ - public Short toJavaShort() { - return new Short((short) toLong()); - } - - /** - * Converts to a java Integer object. - */ - public Integer toJavaInteger() { - return new Integer((int) toLong()); - } - - /** - * Converts to a java Long object. - */ - public Long toJavaLong() { - return new Long((int) toLong()); - } - - /** - * Converts to a java Float object. - */ - public Float toJavaFloat() { - return new Float((float) toDouble()); - } - - /** - * Converts to a java Double object. - */ - public Double toJavaDouble() { - return new Double(toDouble()); - } - - /** - * Converts to a java Character object. - */ - public Character toJavaCharacter() { - return new Character(toChar()); - } - - /** - * Converts to a java String object. - */ - public String toJavaString() { - return toString(); - } - - /** - * Converts to a java Collection object. - */ - public Collection toJavaCollection(Env env, Class type) { - env.warning(L.l("Can't convert {0} to Java {1}", - getClass().getName(), type.getName())); - - return null; - } - - /** - * Converts to a java List object. - */ - public List toJavaList(Env env, Class type) { - env.warning(L.l("Can't convert {0} to Java {1}", - getClass().getName(), type.getName())); - - return null; - } - - /** - * Converts to a java Map object. - */ - public Map toJavaMap(Env env, Class type) { - env.warning(L.l("Can't convert {0} to Java {1}", - getClass().getName(), type.getName())); - - return null; - } - - /** - * Converts to a Java Calendar. - */ - public Calendar toJavaCalendar() { - Calendar cal = Calendar.getInstance(); - - cal.setTimeInMillis(toLong()); - - return cal; - } - - /** - * Converts to a Java Date. - */ - public Date toJavaDate() { - return new Date(toLong()); - } - - /** - * Converts to a Java URL. - */ - public URL toJavaURL(Env env) { - try { - return new URL(toString()); - } catch (MalformedURLException e) { - env.warning(L.l(e.getMessage())); - return null; - } - } - - /** - * Converts to a Java BigDecimal. - */ - public BigDecimal toBigDecimal() { - return new BigDecimal(toString()); - } - - /** - * Converts to a Java BigInteger. - */ - public BigInteger toBigInteger() { - return new BigInteger(toString()); - } - - /** - * Converts to an exception. - */ - public QuercusException toException(Env env, String file, int line) { - putField(env, env.createString("file"), env.createString(file)); - putField(env, env.createString("line"), LongValue.create(line)); - - return new QuercusLanguageException(this); - } - - /** - * Converts to a raw value. - */ - public Value toValue() { - return this; - } - - /** - * Converts to a key. - */ - public Value toKey() { - throw new QuercusRuntimeException(L.l("{0} is not a valid key", this)); - } - - /** - * Convert to a ref. - */ - public Value toRef() { - return this; - } - - /** - * Convert to a function argument value, e.g. for - * - * function foo($a) - * - * where $a is never assigned or modified - */ - public Value toLocalValueReadOnly() { - return this; - } - - /** - * Convert to a function argument value, e.g. for - * - * function foo($a) - * - * where $a is never assigned, but might be modified, e.g. $a[3] = 9 - */ - public Value toLocalValue() { - return this; - } - - /** - * Convert to a function argument value, e.g. for - * - * function foo($a) - * - * where $a may be assigned. - */ - public Value toLocalRef() { - return this; - } - - /** - * Convert to a function argument value, e.g. for - * - * function foo($a) - * - * where $a is used as a variable in the function - */ - public Var toLocalVar() { - return toLocalRef().toVar(); - } - - /** - * Convert to a function argument reference value, e.g. for - * - * function foo(&$a) - * - * where $a is used as a variable in the function - */ - public Var toLocalVarDeclAsRef() { - return new Var(this); - } - - /** - * Converts to a local $this, which can depend on the calling class - */ - public Value toLocalThis(QuercusClass qClass) { - return this; - } - - /** - * Convert to a function argument reference value, e.g. for - * - * function foo(&$a) - * - * where $a is never assigned in the function - */ - public Value toRefValue() { - return this; - } - - /** - * Converts to a Var. - */ - public Var toVar() { - return new Var(this); - } - - /** - * Convert to a function argument reference value, e.g. for - * - * function foo(&$a) - * - * where $a is used as a variable in the function - */ - public Value toArgRef() { - Env.getCurrent().warning(L.l( - "'{0}' is an invalid reference, because only " - + "variables may be passed by reference.", - this)); - - return NullValue.NULL; - } - - /** - * Converts to a StringValue. - */ - public StringValue toStringValue() { - return toStringValue(Env.getInstance()); - } - - /* - * Converts to a StringValue. - */ - public StringValue toStringValue(Env env) { - return toStringBuilder(env); - } - - /** - * Converts to a Unicode string. For unicode.semantics=false, this will - * still return a StringValue. For unicode.semantics=true, this will - * return a UnicodeStringValue. - */ - public StringValue toUnicode(Env env) { - return toUnicodeValue(env); - } - - /** - * Converts to a UnicodeValue for marshaling, so it will create a - * UnicodeValue event when unicode.semantics=false. - */ - public StringValue toUnicodeValue() { - return toUnicodeValue(Env.getInstance()); - } - - /** - * Converts to a UnicodeValue for marshaling, so it will create a - * UnicodeValue event when unicode.semantics=false. - */ - public StringValue toUnicodeValue(Env env) { - // php/0ci0 - return new UnicodeBuilderValue(env.createString(toString())); - } - - /** - * Converts to a BinaryValue. - */ - public StringValue toBinaryValue() { - return toBinaryValue(Env.getInstance()); - } - - /** - * Converts to a BinaryValue. - */ - public StringValue toBinaryValue(String charset) { - return toBinaryValue(); - } - - /** - * Converts to a BinaryValue. - */ - public StringValue toBinaryValue(Env env) { - StringValue bb = env.createBinaryBuilder(); - - bb.append(this); - - return bb; - - /* - try { - int length = 0; - while (true) { - bb.ensureCapacity(bb.getLength() + 256); - - int sublen = is.read(bb.getBuffer(), - bb.getOffset(), - bb.getLength() - bb.getOffset()); - - if (sublen <= 0) - return bb; - else { - length += sublen; - bb.setOffset(length); - } - } - } catch (IOException e) { - throw new QuercusException(e); - } - */ - } - - /** - * Returns a byteArrayInputStream for the value. - * See TempBufferStringValue for how this can be overriden - * - * @return InputStream - */ - public InputStream toInputStream() { - return new StringInputStream(toString()); - } - - /** - * Converts to a string builder - */ - public StringValue toStringBuilder() { - return toStringBuilder(Env.getInstance()); - } - - /** - * Converts to a string builder - */ - public StringValue toStringBuilder(Env env) { - return env.createUnicodeBuilder().appendUnicode(this); - } - - /** - * Converts to a string builder - */ - public StringValue toStringBuilder(Env env, Value value) { - return toStringBuilder(env).appendUnicode(value); - } - - /** - * Converts to a string builder - */ - public StringValue toStringBuilder(Env env, StringValue value) { - return toStringBuilder(env).appendUnicode(value); - } - - /** - * Converts to a string builder - */ - public StringValue copyStringBuilder() { - return toStringBuilder(); - } - - /** - * Converts to a long vaule - */ - public LongValue toLongValue() { - return LongValue.create(toLong()); - } - - /** - * Converts to a double vaule - */ - public DoubleValue toDoubleValue() { - return new DoubleValue(toDouble()); - } - - /** - * Returns true for a callable object. - */ - public boolean isCallable(Env env) { - return false; - } - - /** - * Returns the callable's name for is_callable() - */ - public String getCallableName() { - return null; - } - - /** - * Converts to a callable - */ - public Callable toCallable(Env env) { - env.warning(L.l("Callable: '{0}' is not a valid callable argument", - toString())); - - return new CallbackError(toString()); - } - - // - // Operations - // - /** - * Append to a string builder. - */ - public StringValue appendTo(UnicodeBuilderValue sb) { - return sb.append(toString()); - } - - /** - * Append to a binary builder. - */ - public StringValue appendTo(StringBuilderValue sb) { - return sb.append(toString()); - } - - /** - * Append to a binary builder. - */ - public StringValue appendTo(BinaryBuilderValue sb) { - return sb.appendBytes(toString()); - } - - /** - * Append to a binary builder. - */ - public StringValue appendTo(LargeStringBuilderValue sb) { - return sb.append(toString()); - } - - /** - * Copy for assignment. - */ - public Value copy() { - return this; - } - - /** - * Copy as an array item - */ - public Value copyArrayItem() { - return copy(); - } - - /** - * Copy as a return value - */ - public Value copyReturn() { - // php/3a5d - - return this; - } - - /** - * Copy for serialization - */ - public final Value copy(Env env) { - return copy(env, new IdentityHashMap()); - } - - /** - * Copy for serialization - */ - public Value copy(Env env, IdentityHashMap map) { - return this; - } - - /** - * Copy for serialization - */ - public Value copyTree(Env env, CopyRoot root) { - return this; - } - - /** - * Clone for the clone keyword - */ - public Value clone(Env env) { - return this; - } - - /** - * Copy for saving a method's arguments. - */ - public Value copySaveFunArg() { - return copy(); - } - - /** - * Returns the type. - */ - public String getType() { - return "value"; - } - - /* - * Returns the resource type. - */ - public String getResourceType() { - return null; - } - - /** - * Returns the current key - */ - public Value key() { - return BooleanValue.FALSE; - } - - /** - * Returns the current value - */ - public Value current() { - return BooleanValue.FALSE; - } - - /** - * Returns the next value - */ - public Value next() { - return BooleanValue.FALSE; - } - - /** - * Returns the previous value - */ - public Value prev() { - return BooleanValue.FALSE; - } - - /** - * Returns the end value. - */ - public Value end() { - return BooleanValue.FALSE; - } - - /** - * Returns the array pointer. - */ - public Value reset() { - return BooleanValue.FALSE; - } - - /** - * Shuffles the array. - */ - public Value shuffle() { - return BooleanValue.FALSE; - } - - /** - * Pops the top array element. - */ - public Value pop(Env env) { - env.warning("cannot pop a non-array"); - - return NullValue.NULL; - } - - /** - * Finds the method name. - */ - public AbstractFunction findFunction(String methodName) { - return null; - } - - // - // function invocation - // - /** - * Evaluates the function. - */ - public Value call(Env env, Value[] args) { - Callable call = toCallable(env); - - if (call != null) { - return call.call(env, args); - } else { - return env.warning(L.l("{0} is not a valid function", - this)); - } - } - - /** - * Evaluates the function, returning a reference. - */ - public Value callRef(Env env, Value[] args) { - AbstractFunction fun = env.getFunction(this); - - if (fun != null) { - return fun.callRef(env, args); - } else { - return env.warning(L.l("{0} is not a valid function", - this)); - } - } - - /** - * Evaluates the function, returning a copy - */ - public Value callCopy(Env env, Value[] args) { - AbstractFunction fun = env.getFunction(this); - - if (fun != null) { - return fun.callCopy(env, args); - } else { - return env.warning(L.l("{0} is not a valid function", - this)); - } - } - - /** - * Evaluates the function. - */ - public Value call(Env env) { - return call(env, NULL_ARG_VALUES); - } - - /** - * Evaluates the function. - */ - public Value callRef(Env env) { - return callRef(env, NULL_ARG_VALUES); - } - - /** - * Evaluates the function with an argument . - */ - public Value call(Env env, Value a1) { - return call(env, new Value[]{a1}); - } - - /** - * Evaluates the function with an argument . - */ - public Value callRef(Env env, Value a1) { - return callRef(env, new Value[]{a1}); - } - - /** - * Evaluates the function with arguments - */ - public Value call(Env env, Value a1, Value a2) { - return call(env, new Value[]{a1, a2}); - } - - /** - * Evaluates the function with arguments - */ - public Value callRef(Env env, Value a1, Value a2) { - return callRef(env, new Value[]{a1, a2}); - } - - /** - * Evaluates the function with arguments - */ - public Value call(Env env, Value a1, Value a2, Value a3) { - return call(env, new Value[]{a1, a2, a3}); - } - - /** - * Evaluates the function with arguments - */ - public Value callRef(Env env, Value a1, Value a2, Value a3) { - return callRef(env, new Value[]{a1, a2, a3}); - } - - /** - * Evaluates the function with arguments - */ - public Value call(Env env, Value a1, Value a2, Value a3, Value a4) { - return call(env, new Value[]{a1, a2, a3, a4}); - } - - /** - * Evaluates the function with arguments - */ - public Value callRef(Env env, Value a1, Value a2, Value a3, Value a4) { - return callRef(env, new Value[]{a1, a2, a3, a4}); - } - - /** - * Evaluates the function with arguments - */ - public Value call(Env env, Value a1, Value a2, Value a3, Value a4, Value a5) { - return call(env, new Value[]{a1, a2, a3, a4, a5}); - } - - /** - * Evaluates the function with arguments - */ - public Value callRef(Env env, - Value a1, Value a2, Value a3, Value a4, Value a5) { - return callRef(env, new Value[]{a1, a2, a3, a4, a5}); - } - - // - // Methods invocation - // - /** - * Evaluates a method. - */ - public Value callMethod(Env env, - StringValue methodName, int hash, - Value[] args) { - if (isNull()) { - return env.error(L.l("Method call '{0}' is not allowed for a null value.", - methodName)); - } else { - return env.error(L.l("'{0}' is an unknown method of {1}.", - methodName, - toDebugString())); - } - } - - /** - * Evaluates a method. - */ - public final Value callMethod(Env env, - StringValue methodName, - Value[] args) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethod(env, methodName, hash, args); - } - - /** - * Evaluates a method. - */ - public Value callMethodRef(Env env, - StringValue methodName, int hash, - Value[] args) { - return callMethod(env, methodName, hash, args); - } - - /** - * Evaluates a method. - */ - public final Value callMethodRef(Env env, - StringValue methodName, - Value[] args) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethodRef(env, methodName, hash, args); - } - - /** - * Evaluates a method with 0 args. - */ - public Value callMethod(Env env, StringValue methodName, int hash) { - return callMethod(env, methodName, hash, NULL_ARG_VALUES); - } - - /** - * Evaluates a method with 0 args. - */ - public final Value callMethod(Env env, StringValue methodName) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethod(env, methodName, hash); - } - - /** - * Evaluates a method with 0 args. - */ - public Value callMethodRef(Env env, StringValue methodName, int hash) { - return callMethodRef(env, methodName, hash, NULL_ARG_VALUES); - } - - /** - * Evaluates a method with 0 args. - */ - public final Value callMethodRef(Env env, StringValue methodName) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethodRef(env, methodName, hash); - } - - /** - * Evaluates a method with 1 arg. - */ - public Value callMethod(Env env, - StringValue methodName, int hash, - Value a1) { - return callMethod(env, methodName, hash, new Value[]{a1}); - } - - /** - * Evaluates a method with 1 arg. - */ - public final Value callMethod(Env env, - StringValue methodName, - Value a1) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethod(env, methodName, hash, a1); - } - - /** - * Evaluates a method with 1 arg. - */ - public Value callMethodRef(Env env, - StringValue methodName, int hash, - Value a1) { - return callMethodRef(env, methodName, hash, new Value[]{a1}); - } - - /** - * Evaluates a method with 1 arg. - */ - public final Value callMethodRef(Env env, - StringValue methodName, - Value a1) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethodRef(env, methodName, hash, a1); - } - - /** - * Evaluates a method with 2 args. - */ - public Value callMethod(Env env, - StringValue methodName, int hash, - Value a1, Value a2) { - return callMethod(env, methodName, hash, new Value[]{a1, a2}); - } - - /** - * Evaluates a method with 2 args. - */ - public final Value callMethod(Env env, - StringValue methodName, - Value a1, Value a2) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethod(env, methodName, hash, - a1, a2); - } - - /** - * Evaluates a method with 2 args. - */ - public Value callMethodRef(Env env, - StringValue methodName, int hash, - Value a1, Value a2) { - return callMethodRef(env, methodName, hash, new Value[]{a1, a2}); - } - - /** - * Evaluates a method with 2 args. - */ - public final Value callMethodRef(Env env, - StringValue methodName, - Value a1, Value a2) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethodRef(env, methodName, hash, - a1, a2); - } - - /** - * Evaluates a method with 3 args. - */ - public Value callMethod(Env env, - StringValue methodName, int hash, - Value a1, Value a2, Value a3) { - return callMethod(env, methodName, hash, new Value[]{a1, a2, a3}); - } - - /** - * Evaluates a method with 3 args. - */ - public final Value callMethod(Env env, - StringValue methodName, - Value a1, Value a2, Value a3) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethod(env, methodName, hash, - a1, a2, a3); - } - - /** - * Evaluates a method with 3 args. - */ - public Value callMethodRef(Env env, - StringValue methodName, int hash, - Value a1, Value a2, Value a3) { - return callMethodRef(env, methodName, hash, new Value[]{a1, a2, a3}); - } - - /** - * Evaluates a method with 3 args. - */ - public final Value callMethodRef(Env env, - StringValue methodName, - Value a1, Value a2, Value a3) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethodRef(env, methodName, hash, - a1, a2, a3); - } - - /** - * Evaluates a method with 4 args. - */ - public Value callMethod(Env env, - StringValue methodName, int hash, - Value a1, Value a2, Value a3, Value a4) { - return callMethod(env, methodName, hash, - new Value[]{a1, a2, a3, a4}); - } - - /** - * Evaluates a method with 4 args. - */ - public final Value callMethod(Env env, - StringValue methodName, - Value a1, Value a2, Value a3, Value a4) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethod(env, methodName, hash, - a1, a2, a3, a4); - } - - /** - * Evaluates a method with 4 args. - */ - public Value callMethodRef(Env env, - StringValue methodName, int hash, - Value a1, Value a2, Value a3, Value a4) { - return callMethodRef(env, methodName, hash, - new Value[]{a1, a2, a3, a4}); - } - - /** - * Evaluates a method with 4 args. - */ - public final Value callMethodRef(Env env, - StringValue methodName, - Value a1, Value a2, Value a3, Value a4) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethodRef(env, methodName, hash, - a1, a2, a3, a4); - } - - /** - * Evaluates a method with 5 args. - */ - public Value callMethod(Env env, - StringValue methodName, int hash, - Value a1, Value a2, Value a3, Value a4, Value a5) { - return callMethod(env, methodName, hash, - new Value[]{a1, a2, a3, a4, a5}); - } - - /** - * Evaluates a method with 5 args. - */ - public final Value callMethod(Env env, - StringValue methodName, - Value a1, Value a2, Value a3, Value a4, Value a5) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethod(env, methodName, hash, - a1, a2, a3, a4, a5); - } - - /** - * Evaluates a method with 5 args. - */ - public Value callMethodRef(Env env, - StringValue methodName, int hash, - Value a1, Value a2, Value a3, Value a4, Value a5) { - return callMethodRef(env, methodName, hash, - new Value[]{a1, a2, a3, a4, a5}); - } - - /** - * Evaluates a method with 5 args. - */ - public final Value callMethodRef(Env env, - StringValue methodName, - Value a1, Value a2, Value a3, Value a4, Value a5) { - int hash = methodName.hashCodeCaseInsensitive(); - - return callMethodRef(env, methodName, hash, - a1, a2, a3, a4, a5); - } - - // - // Methods from StringValue - // - /** - * Evaluates a method. - */ - private Value callClassMethod(Env env, AbstractFunction fun, Value[] args) { - return NullValue.NULL; - } - - private Value errorNoMethod(Env env, char[] name, int nameLen) { - String methodName = new String(name, 0, nameLen); - - if (isNull()) { - return env.error(L.l("Method call '{0}' is not allowed for a null value.", - methodName)); - } else { - return env.error(L.l("'{0}' is an unknown method of {1}.", - methodName, - toDebugString())); - } - } - - // - // Arithmetic operations - // - /** - * Negates the value. - */ - public Value neg() { - return LongValue.create(-toLong()); - } - - /** - * Negates the value. - */ - public Value pos() { - return LongValue.create(toLong()); - } - - /** - * Adds to the following value. - */ - public Value add(Value rValue) { - if (getValueType().isLongAdd() && rValue.getValueType().isLongAdd()) { - return LongValue.create(toLong() + rValue.toLong()); - } - - return DoubleValue.create(toDouble() + rValue.toDouble()); - } - - /** - * Multiplies to the following value. - */ - public Value add(long lLong) { - return new DoubleValue(lLong + toDouble()); - } - - /** - * Pre-increment the following value. - */ - public Value preincr(int incr) { - return increment(incr); - } - - /** - * Post-increment the following value. - */ - public Value postincr(int incr) { - return increment(incr); - } - - /** - * Return the next integer - */ - public Value addOne() { - return add(1); - } - - /** - * Return the previous integer - */ - public Value subOne() { - return sub(1); - } - - /** - * Pre-increment the following value. - */ - public Value preincr() { - return increment(1); - } - - /** - * Post-increment the following value. - */ - public Value postincr() { - return increment(1); - } - - /** - * Pre-increment the following value. - */ - public Value predecr() { - return increment(-1); - } - - /** - * Post-increment the following value. - */ - public Value postdecr() { - return increment(-1); - } - - /** - * Increment the following value. - */ - public Value increment(int incr) { - long lValue = toLong(); - - return LongValue.create(lValue + incr); - } - - /** - * Subtracts to the following value. - */ - public Value sub(Value rValue) { - if (getValueType().isLongAdd() && rValue.getValueType().isLongAdd()) { - return LongValue.create(toLong() - rValue.toLong()); - } - - return DoubleValue.create(toDouble() - rValue.toDouble()); - } - - /** - * Subtracts - */ - public Value sub(long rLong) { - return new DoubleValue(toDouble() - rLong); - } - - /** - * Substracts from the previous value. - */ - public Value sub_rev(long lLong) { - if (getValueType().isLongAdd()) { - return LongValue.create(lLong - toLong()); - } else { - return new DoubleValue(lLong - toDouble()); - } - } - - /** - * Multiplies to the following value. - */ - public Value mul(Value rValue) { - if (getValueType().isLongAdd() && rValue.getValueType().isLongAdd()) { - return LongValue.create(toLong() * rValue.toLong()); - } else { - return new DoubleValue(toDouble() * rValue.toDouble()); - } - } - - /** - * Multiplies to the following value. - */ - public Value mul(long r) { - if (isLongConvertible()) { - return LongValue.create(toLong() * r); - } else { - return new DoubleValue(toDouble() * r); - } - } - - /** - * Divides the following value. - */ - public Value div(Value rValue) { - if (getValueType().isLongAdd() && rValue.getValueType().isLongAdd()) { - long l = toLong(); - long r = rValue.toLong(); - - if (r != 0 && l % r == 0) { - return LongValue.create(l / r); - } else { + } + if (rVal instanceof ObjectValue) { + return -1; + } + + // TODO: proper default case? + throw new RuntimeException( + "values are incomparable: " + lVal + " <=> " + rVal); + } + + /** + * Returns true for less than + */ + public boolean lt(Value rValue) { + return cmp(rValue) < 0; + } + + /** + * Returns true for less than or equal to + */ + public boolean leq(Value rValue) { + return cmp(rValue) <= 0; + } + + /** + * Returns true for greater than + */ + public boolean gt(Value rValue) { + return cmp(rValue) > 0; + } + + /** + * Returns true for greater than or equal to + */ + public boolean geq(Value rValue) { + return cmp(rValue) >= 0; + } + + // + // Conversions + // + /** + * Converts to a boolean. + */ + public boolean toBoolean() { + return true; + } + + /** + * Converts to a long. + */ + public long toLong() { + return toBoolean() ? 1 : 0; + } + + /** + * Converts to an int + */ + public int toInt() { + return (int) toLong(); + } + + /** + * Converts to a double. + */ + public double toDouble() { + return 0; + } + + /** + * Converts to a char + */ + public char toChar() { + String s = toString(); + + if (s == null || s.length() < 1) { + return 0; + } else { + return s.charAt(0); + } + } + + /** + * Converts to a string. + * + * @param env + */ + public StringValue toString(Env env) { + return toStringValue(); + } + + /** + * Converts to an array. + */ + public Value toArray() { + return new ArrayValueImpl().append(this); + } + + /** + * Converts to an array if null. + */ + public Value toAutoArray() { + Env.getCurrent().warning(L.l("'{0}' cannot be used as an array.", + toDebugString())); + + return this; + } + + /** + * Casts to an array. + */ + public ArrayValue toArrayValue(Env env) { + env.warning(L.l("'{0}' ({1}) is not assignable to ArrayValue", + this, getType())); + + return null; + } + + /** + * Converts to an object if null. + */ + public Value toAutoObject(Env env) { + return this; + } + + /** + * Converts to an object. + */ + public Value toObject(Env env) { + ObjectValue obj = env.createObject(); + + obj.putField(env, env.createString("scalar"), this); + + return obj; + } + + /** + * Converts to a java object. + */ + public Object toJavaObject() { + return null; + } + + /** + * Converts to a java object. + */ + public Object toJavaObject(Env env, Class type) { + env.warning(L.l("Can't convert {0} to Java {1}", + getClass().getName(), type.getName())); + + return null; + } + + /** + * Converts to a java object. + */ + public Object toJavaObjectNotNull(Env env, Class type) { + env.warning(L.l("Can't convert {0} to Java {1}", + getClass().getName(), type.getName())); + + return null; + } + + /** + * Converts to a java boolean object. + */ + public Boolean toJavaBoolean() { + return toBoolean() ? Boolean.TRUE : Boolean.FALSE; + } + + /** + * Converts to a java byte object. + */ + public Byte toJavaByte() { + return new Byte((byte) toLong()); + } + + /** + * Converts to a java short object. + */ + public Short toJavaShort() { + return new Short((short) toLong()); + } + + /** + * Converts to a java Integer object. + */ + public Integer toJavaInteger() { + return new Integer((int) toLong()); + } + + /** + * Converts to a java Long object. + */ + public Long toJavaLong() { + return new Long((int) toLong()); + } + + /** + * Converts to a java Float object. + */ + public Float toJavaFloat() { + return new Float((float) toDouble()); + } + + /** + * Converts to a java Double object. + */ + public Double toJavaDouble() { + return new Double(toDouble()); + } + + /** + * Converts to a java Character object. + */ + public Character toJavaCharacter() { + return new Character(toChar()); + } + + /** + * Converts to a java String object. + */ + public String toJavaString() { + return toString(); + } + + /** + * Converts to a java Collection object. + */ + public Collection toJavaCollection(Env env, Class type) { + env.warning(L.l("Can't convert {0} to Java {1}", + getClass().getName(), type.getName())); + + return null; + } + + /** + * Converts to a java List object. + */ + public List toJavaList(Env env, Class type) { + env.warning(L.l("Can't convert {0} to Java {1}", + getClass().getName(), type.getName())); + + return null; + } + + /** + * Converts to a java Map object. + */ + public Map toJavaMap(Env env, Class type) { + env.warning(L.l("Can't convert {0} to Java {1}", + getClass().getName(), type.getName())); + + return null; + } + + /** + * Converts to a Java Calendar. + */ + public Calendar toJavaCalendar() { + Calendar cal = Calendar.getInstance(); + + cal.setTimeInMillis(toLong()); + + return cal; + } + + /** + * Converts to a Java Date. + */ + public Date toJavaDate() { + return new Date(toLong()); + } + + /** + * Converts to a Java URL. + */ + public URL toJavaURL(Env env) { + try { + return new URL(toString()); + } catch (MalformedURLException e) { + env.warning(L.l(e.getMessage())); + return null; + } + } + + /** + * Converts to a Java BigDecimal. + */ + public BigDecimal toBigDecimal() { + return new BigDecimal(toString()); + } + + /** + * Converts to a Java BigInteger. + */ + public BigInteger toBigInteger() { + return new BigInteger(toString()); + } + + /** + * Converts to an exception. + */ + public QuercusException toException(Env env, String file, int line) { + putField(env, env.createString("file"), env.createString(file)); + putField(env, env.createString("line"), LongValue.create(line)); + + return new QuercusLanguageException(this); + } + + /** + * Converts to a raw value. + */ + public Value toValue() { + return this; + } + + /** + * Converts to a key. + */ + public Value toKey() { + throw new QuercusRuntimeException(L.l("{0} is not a valid key", this)); + } + + /** + * Convert to a ref. + */ + public Value toRef() { + return this; + } + + /** + * Convert to a function argument value, e.g. for + * + * function foo($a) + * + * where $a is never assigned or modified + */ + public Value toLocalValueReadOnly() { + return this; + } + + /** + * Convert to a function argument value, e.g. for + * + * function foo($a) + * + * where $a is never assigned, but might be modified, e.g. $a[3] = 9 + */ + public Value toLocalValue() { + return this; + } + + /** + * Convert to a function argument value, e.g. for + * + * function foo($a) + * + * where $a may be assigned. + */ + public Value toLocalRef() { + return this; + } + + /** + * Convert to a function argument value, e.g. for + * + * function foo($a) + * + * where $a is used as a variable in the function + */ + public Var toLocalVar() { + return toLocalRef().toVar(); + } + + /** + * Convert to a function argument reference value, e.g. for + * + * function foo(&$a) + * + * where $a is used as a variable in the function + */ + public Var toLocalVarDeclAsRef() { + return new Var(this); + } + + /** + * Converts to a local $this, which can depend on the calling class + */ + public Value toLocalThis(QuercusClass qClass) { + return this; + } + + /** + * Convert to a function argument reference value, e.g. for + * + * function foo(&$a) + * + * where $a is never assigned in the function + */ + public Value toRefValue() { + return this; + } + + /** + * Converts to a Var. + */ + public Var toVar() { + return new Var(this); + } + + /** + * Convert to a function argument reference value, e.g. for + * + * function foo(&$a) + * + * where $a is used as a variable in the function + */ + public Value toArgRef() { + Env.getCurrent().warning(L.l( + "'{0}' is an invalid reference, because only " + + "variables may be passed by reference.", + this)); + + return NullValue.NULL; + } + + /** + * Converts to a StringValue. + */ + public StringValue toStringValue() { + return toStringValue(Env.getInstance()); + } + + /* + * Converts to a StringValue. + */ + public StringValue toStringValue(Env env) { + return toStringBuilder(env); + } + + /** + * Converts to a Unicode string. For unicode.semantics=false, this will + * still return a StringValue. For unicode.semantics=true, this will + * return a UnicodeStringValue. + */ + public StringValue toUnicode(Env env) { + return toUnicodeValue(env); + } + + /** + * Converts to a UnicodeValue for marshaling, so it will create a + * UnicodeValue event when unicode.semantics=false. + */ + public StringValue toUnicodeValue() { + return toUnicodeValue(Env.getInstance()); + } + + /** + * Converts to a UnicodeValue for marshaling, so it will create a + * UnicodeValue event when unicode.semantics=false. + */ + public StringValue toUnicodeValue(Env env) { + // php/0ci0 + return new UnicodeBuilderValue(env.createString(toString())); + } + + /** + * Converts to a BinaryValue. + */ + public StringValue toBinaryValue() { + return toBinaryValue(Env.getInstance()); + } + + /** + * Converts to a BinaryValue. + */ + public StringValue toBinaryValue(String charset) { + return toBinaryValue(); + } + + /** + * Converts to a BinaryValue. + */ + public StringValue toBinaryValue(Env env) { + StringValue bb = env.createBinaryBuilder(); + + bb.append(this); + + return bb; + + /* + try { + int length = 0; + while (true) { + bb.ensureCapacity(bb.getLength() + 256); + + int sublen = is.read(bb.getBuffer(), + bb.getOffset(), + bb.getLength() - bb.getOffset()); + + if (sublen <= 0) + return bb; + else { + length += sublen; + bb.setOffset(length); + } + } + } catch (IOException e) { + throw new QuercusException(e); + } + */ + } + + /** + * Returns a byteArrayInputStream for the value. + * See TempBufferStringValue for how this can be overriden + * + * @return InputStream + */ + public InputStream toInputStream() { + return new StringInputStream(toString()); + } + + /** + * Converts to a string builder + */ + public StringValue toStringBuilder() { + return toStringBuilder(Env.getInstance()); + } + + /** + * Converts to a string builder + */ + public StringValue toStringBuilder(Env env) { + return env.createUnicodeBuilder().appendUnicode(this); + } + + /** + * Converts to a string builder + */ + public StringValue toStringBuilder(Env env, Value value) { + return toStringBuilder(env).appendUnicode(value); + } + + /** + * Converts to a string builder + */ + public StringValue toStringBuilder(Env env, StringValue value) { + return toStringBuilder(env).appendUnicode(value); + } + + /** + * Converts to a string builder + */ + public StringValue copyStringBuilder() { + return toStringBuilder(); + } + + /** + * Converts to a long vaule + */ + public LongValue toLongValue() { + return LongValue.create(toLong()); + } + + /** + * Converts to a double vaule + */ + public DoubleValue toDoubleValue() { + return new DoubleValue(toDouble()); + } + + /** + * Returns true for a callable object. + */ + public boolean isCallable(Env env) { + return false; + } + + /** + * Returns the callable's name for is_callable() + */ + public String getCallableName() { + return null; + } + + /** + * Converts to a callable + */ + public Callable toCallable(Env env) { + env.warning(L.l("Callable: '{0}' is not a valid callable argument", + toString())); + + return new CallbackError(toString()); + } + + // + // Operations + // + /** + * Append to a string builder. + */ + public StringValue appendTo(UnicodeBuilderValue sb) { + return sb.append(toString()); + } + + /** + * Append to a binary builder. + */ + public StringValue appendTo(StringBuilderValue sb) { + return sb.append(toString()); + } + + /** + * Append to a binary builder. + */ + public StringValue appendTo(BinaryBuilderValue sb) { + return sb.appendBytes(toString()); + } + + /** + * Append to a binary builder. + */ + public StringValue appendTo(LargeStringBuilderValue sb) { + return sb.append(toString()); + } + + /** + * Copy for assignment. + */ + public Value copy() { + return this; + } + + /** + * Copy as an array item + */ + public Value copyArrayItem() { + return copy(); + } + + /** + * Copy as a return value + */ + public Value copyReturn() { + // php/3a5d + + return this; + } + + /** + * Copy for serialization + */ + public final Value copy(Env env) { + return copy(env, new IdentityHashMap()); + } + + /** + * Copy for serialization + */ + public Value copy(Env env, IdentityHashMap map) { + return this; + } + + /** + * Copy for serialization + */ + public Value copyTree(Env env, CopyRoot root) { + return this; + } + + /** + * Clone for the clone keyword + */ + public Value clone(Env env) { + return this; + } + + /** + * Copy for saving a method's arguments. + */ + public Value copySaveFunArg() { + return copy(); + } + + /** + * Returns the type. + */ + public String getType() { + return "value"; + } + + /* + * Returns the resource type. + */ + public String getResourceType() { + return null; + } + + /** + * Returns the current key + */ + public Value key() { + return BooleanValue.FALSE; + } + + /** + * Returns the current value + */ + public Value current() { + return BooleanValue.FALSE; + } + + /** + * Returns the next value + */ + public Value next() { + return BooleanValue.FALSE; + } + + /** + * Returns the previous value + */ + public Value prev() { + return BooleanValue.FALSE; + } + + /** + * Returns the end value. + */ + public Value end() { + return BooleanValue.FALSE; + } + + /** + * Returns the array pointer. + */ + public Value reset() { + return BooleanValue.FALSE; + } + + /** + * Shuffles the array. + */ + public Value shuffle() { + return BooleanValue.FALSE; + } + + /** + * Pops the top array element. + */ + public Value pop(Env env) { + env.warning("cannot pop a non-array"); + + return NullValue.NULL; + } + + /** + * Finds the method name. + */ + public AbstractFunction findFunction(String methodName) { + return null; + } + + // + // function invocation + // + /** + * Evaluates the function. + */ + public Value call(Env env, Value[] args) { + Callable call = toCallable(env); + + if (call != null) { + return call.call(env, args); + } else { + return env.warning(L.l("{0} is not a valid function", + this)); + } + } + + /** + * Evaluates the function, returning a reference. + */ + public Value callRef(Env env, Value[] args) { + AbstractFunction fun = env.getFunction(this); + + if (fun != null) { + return fun.callRef(env, args); + } else { + return env.warning(L.l("{0} is not a valid function", + this)); + } + } + + /** + * Evaluates the function, returning a copy + */ + public Value callCopy(Env env, Value[] args) { + AbstractFunction fun = env.getFunction(this); + + if (fun != null) { + return fun.callCopy(env, args); + } else { + return env.warning(L.l("{0} is not a valid function", + this)); + } + } + + /** + * Evaluates the function. + */ + public Value call(Env env) { + return call(env, NULL_ARG_VALUES); + } + + /** + * Evaluates the function. + */ + public Value callRef(Env env) { + return callRef(env, NULL_ARG_VALUES); + } + + /** + * Evaluates the function with an argument . + */ + public Value call(Env env, Value a1) { + return call(env, new Value[]{a1}); + } + + /** + * Evaluates the function with an argument . + */ + public Value callRef(Env env, Value a1) { + return callRef(env, new Value[]{a1}); + } + + /** + * Evaluates the function with arguments + */ + public Value call(Env env, Value a1, Value a2) { + return call(env, new Value[]{a1, a2}); + } + + /** + * Evaluates the function with arguments + */ + public Value callRef(Env env, Value a1, Value a2) { + return callRef(env, new Value[]{a1, a2}); + } + + /** + * Evaluates the function with arguments + */ + public Value call(Env env, Value a1, Value a2, Value a3) { + return call(env, new Value[]{a1, a2, a3}); + } + + /** + * Evaluates the function with arguments + */ + public Value callRef(Env env, Value a1, Value a2, Value a3) { + return callRef(env, new Value[]{a1, a2, a3}); + } + + /** + * Evaluates the function with arguments + */ + public Value call(Env env, Value a1, Value a2, Value a3, Value a4) { + return call(env, new Value[]{a1, a2, a3, a4}); + } + + /** + * Evaluates the function with arguments + */ + public Value callRef(Env env, Value a1, Value a2, Value a3, Value a4) { + return callRef(env, new Value[]{a1, a2, a3, a4}); + } + + /** + * Evaluates the function with arguments + */ + public Value call(Env env, Value a1, Value a2, Value a3, Value a4, Value a5) { + return call(env, new Value[]{a1, a2, a3, a4, a5}); + } + + /** + * Evaluates the function with arguments + */ + public Value callRef(Env env, + Value a1, Value a2, Value a3, Value a4, Value a5) { + return callRef(env, new Value[]{a1, a2, a3, a4, a5}); + } + + // + // Methods invocation + // + /** + * Evaluates a method. + */ + public Value callMethod(Env env, + StringValue methodName, int hash, + Value[] args) { + if (isNull()) { + return env.error(L.l("Method call '{0}' is not allowed for a null value.", + methodName)); + } else { + return env.error(L.l("'{0}' is an unknown method of {1}.", + methodName, + toDebugString())); + } + } + + /** + * Evaluates a method. + */ + public final Value callMethod(Env env, + StringValue methodName, + Value[] args) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethod(env, methodName, hash, args); + } + + /** + * Evaluates a method. + */ + public Value callMethodRef(Env env, + StringValue methodName, int hash, + Value[] args) { + return callMethod(env, methodName, hash, args); + } + + /** + * Evaluates a method. + */ + public final Value callMethodRef(Env env, + StringValue methodName, + Value[] args) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethodRef(env, methodName, hash, args); + } + + /** + * Evaluates a method with 0 args. + */ + public Value callMethod(Env env, StringValue methodName, int hash) { + return callMethod(env, methodName, hash, NULL_ARG_VALUES); + } + + /** + * Evaluates a method with 0 args. + */ + public final Value callMethod(Env env, StringValue methodName) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethod(env, methodName, hash); + } + + /** + * Evaluates a method with 0 args. + */ + public Value callMethodRef(Env env, StringValue methodName, int hash) { + return callMethodRef(env, methodName, hash, NULL_ARG_VALUES); + } + + /** + * Evaluates a method with 0 args. + */ + public final Value callMethodRef(Env env, StringValue methodName) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethodRef(env, methodName, hash); + } + + /** + * Evaluates a method with 1 arg. + */ + public Value callMethod(Env env, + StringValue methodName, int hash, + Value a1) { + return callMethod(env, methodName, hash, new Value[]{a1}); + } + + /** + * Evaluates a method with 1 arg. + */ + public final Value callMethod(Env env, + StringValue methodName, + Value a1) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethod(env, methodName, hash, a1); + } + + /** + * Evaluates a method with 1 arg. + */ + public Value callMethodRef(Env env, + StringValue methodName, int hash, + Value a1) { + return callMethodRef(env, methodName, hash, new Value[]{a1}); + } + + /** + * Evaluates a method with 1 arg. + */ + public final Value callMethodRef(Env env, + StringValue methodName, + Value a1) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethodRef(env, methodName, hash, a1); + } + + /** + * Evaluates a method with 2 args. + */ + public Value callMethod(Env env, + StringValue methodName, int hash, + Value a1, Value a2) { + return callMethod(env, methodName, hash, new Value[]{a1, a2}); + } + + /** + * Evaluates a method with 2 args. + */ + public final Value callMethod(Env env, + StringValue methodName, + Value a1, Value a2) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethod(env, methodName, hash, + a1, a2); + } + + /** + * Evaluates a method with 2 args. + */ + public Value callMethodRef(Env env, + StringValue methodName, int hash, + Value a1, Value a2) { + return callMethodRef(env, methodName, hash, new Value[]{a1, a2}); + } + + /** + * Evaluates a method with 2 args. + */ + public final Value callMethodRef(Env env, + StringValue methodName, + Value a1, Value a2) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethodRef(env, methodName, hash, + a1, a2); + } + + /** + * Evaluates a method with 3 args. + */ + public Value callMethod(Env env, + StringValue methodName, int hash, + Value a1, Value a2, Value a3) { + return callMethod(env, methodName, hash, new Value[]{a1, a2, a3}); + } + + /** + * Evaluates a method with 3 args. + */ + public final Value callMethod(Env env, + StringValue methodName, + Value a1, Value a2, Value a3) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethod(env, methodName, hash, + a1, a2, a3); + } + + /** + * Evaluates a method with 3 args. + */ + public Value callMethodRef(Env env, + StringValue methodName, int hash, + Value a1, Value a2, Value a3) { + return callMethodRef(env, methodName, hash, new Value[]{a1, a2, a3}); + } + + /** + * Evaluates a method with 3 args. + */ + public final Value callMethodRef(Env env, + StringValue methodName, + Value a1, Value a2, Value a3) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethodRef(env, methodName, hash, + a1, a2, a3); + } + + /** + * Evaluates a method with 4 args. + */ + public Value callMethod(Env env, + StringValue methodName, int hash, + Value a1, Value a2, Value a3, Value a4) { + return callMethod(env, methodName, hash, + new Value[]{a1, a2, a3, a4}); + } + + /** + * Evaluates a method with 4 args. + */ + public final Value callMethod(Env env, + StringValue methodName, + Value a1, Value a2, Value a3, Value a4) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethod(env, methodName, hash, + a1, a2, a3, a4); + } + + /** + * Evaluates a method with 4 args. + */ + public Value callMethodRef(Env env, + StringValue methodName, int hash, + Value a1, Value a2, Value a3, Value a4) { + return callMethodRef(env, methodName, hash, + new Value[]{a1, a2, a3, a4}); + } + + /** + * Evaluates a method with 4 args. + */ + public final Value callMethodRef(Env env, + StringValue methodName, + Value a1, Value a2, Value a3, Value a4) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethodRef(env, methodName, hash, + a1, a2, a3, a4); + } + + /** + * Evaluates a method with 5 args. + */ + public Value callMethod(Env env, + StringValue methodName, int hash, + Value a1, Value a2, Value a3, Value a4, Value a5) { + return callMethod(env, methodName, hash, + new Value[]{a1, a2, a3, a4, a5}); + } + + /** + * Evaluates a method with 5 args. + */ + public final Value callMethod(Env env, + StringValue methodName, + Value a1, Value a2, Value a3, Value a4, Value a5) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethod(env, methodName, hash, + a1, a2, a3, a4, a5); + } + + /** + * Evaluates a method with 5 args. + */ + public Value callMethodRef(Env env, + StringValue methodName, int hash, + Value a1, Value a2, Value a3, Value a4, Value a5) { + return callMethodRef(env, methodName, hash, + new Value[]{a1, a2, a3, a4, a5}); + } + + /** + * Evaluates a method with 5 args. + */ + public final Value callMethodRef(Env env, + StringValue methodName, + Value a1, Value a2, Value a3, Value a4, Value a5) { + int hash = methodName.hashCodeCaseInsensitive(); + + return callMethodRef(env, methodName, hash, + a1, a2, a3, a4, a5); + } + + // + // Methods from StringValue + // + /** + * Evaluates a method. + */ + private Value callClassMethod(Env env, AbstractFunction fun, Value[] args) { + return NullValue.NULL; + } + + private Value errorNoMethod(Env env, char[] name, int nameLen) { + String methodName = new String(name, 0, nameLen); + + if (isNull()) { + return env.error(L.l("Method call '{0}' is not allowed for a null value.", + methodName)); + } else { + return env.error(L.l("'{0}' is an unknown method of {1}.", + methodName, + toDebugString())); + } + } + + // + // Arithmetic operations + // + /** + * Negates the value. + */ + public Value neg() { + return LongValue.create(-toLong()); + } + + /** + * Negates the value. + */ + public Value pos() { + return LongValue.create(toLong()); + } + + /** + * Adds to the following value. + */ + public Value add(Value rValue) { + if (getValueType().isLongAdd() && rValue.getValueType().isLongAdd()) { + return LongValue.create(toLong() + rValue.toLong()); + } + + return DoubleValue.create(toDouble() + rValue.toDouble()); + } + + /** + * Multiplies to the following value. + */ + public Value add(long lLong) { + return new DoubleValue(lLong + toDouble()); + } + + /** + * Pre-increment the following value. + */ + public Value preincr(int incr) { + return increment(incr); + } + + /** + * Post-increment the following value. + */ + public Value postincr(int incr) { + return increment(incr); + } + + /** + * Return the next integer + */ + public Value addOne() { + return add(1); + } + + /** + * Return the previous integer + */ + public Value subOne() { + return sub(1); + } + + /** + * Pre-increment the following value. + */ + public Value preincr() { + return increment(1); + } + + /** + * Post-increment the following value. + */ + public Value postincr() { + return increment(1); + } + + /** + * Pre-increment the following value. + */ + public Value predecr() { + return increment(-1); + } + + /** + * Post-increment the following value. + */ + public Value postdecr() { + return increment(-1); + } + + /** + * Increment the following value. + */ + public Value increment(int incr) { + long lValue = toLong(); + + return LongValue.create(lValue + incr); + } + + /** + * Subtracts to the following value. + */ + public Value sub(Value rValue) { + if (getValueType().isLongAdd() && rValue.getValueType().isLongAdd()) { + return LongValue.create(toLong() - rValue.toLong()); + } + + return DoubleValue.create(toDouble() - rValue.toDouble()); + } + + /** + * Subtracts + */ + public Value sub(long rLong) { + return new DoubleValue(toDouble() - rLong); + } + + /** + * Substracts from the previous value. + */ + public Value sub_rev(long lLong) { + if (getValueType().isLongAdd()) { + return LongValue.create(lLong - toLong()); + } else { + return new DoubleValue(lLong - toDouble()); + } + } + + /** + * Multiplies to the following value. + */ + public Value mul(Value rValue) { + if (getValueType().isLongAdd() && rValue.getValueType().isLongAdd()) { + return LongValue.create(toLong() * rValue.toLong()); + } else { + return new DoubleValue(toDouble() * rValue.toDouble()); + } + } + + /** + * Multiplies to the following value. + */ + public Value mul(long r) { + if (isLongConvertible()) { + return LongValue.create(toLong() * r); + } else { + return new DoubleValue(toDouble() * r); + } + } + + /** + * Divides the following value. + */ + public Value div(Value rValue) { + if (getValueType().isLongAdd() && rValue.getValueType().isLongAdd()) { + long l = toLong(); + long r = rValue.toLong(); + + if (r != 0 && l % r == 0) { + return LongValue.create(l / r); + } else { + return new DoubleValue(toDouble() / rValue.toDouble()); + } + } else { return new DoubleValue(toDouble() / rValue.toDouble()); - } - } else { - return new DoubleValue(toDouble() / rValue.toDouble()); - } - } - - /** - * Multiplies to the following value. - */ - public Value div(long r) { - long l = toLong(); - - if (r != 0 && l % r == 0) { - return LongValue.create(l / r); - } else { - return new DoubleValue(toDouble() / r); - } - } - - /** - * modulo the following value. - */ - public Value mod(Value rValue) { - double lDouble = toDouble(); - double rDouble = rValue.toDouble(); - - return LongValue.create((long) lDouble % rDouble); - } - - /** - * Shifts left by the value. - */ - public Value lshift(Value rValue) { - long lLong = toLong(); - long rLong = rValue.toLong(); - - return LongValue.create(lLong << rLong); - } - - /** - * Shifts right by the value. - */ - public Value rshift(Value rValue) { - long lLong = toLong(); - long rLong = rValue.toLong(); - - return LongValue.create(lLong >> rLong); - } - - /* - * Binary And. - */ - public Value bitAnd(Value rValue) { - return LongValue.create(toLong() & rValue.toLong()); - } - - /* - * Binary or. - */ - public Value bitOr(Value rValue) { - return LongValue.create(toLong() | rValue.toLong()); - } - - /** - * Binary xor. - */ - public Value bitXor(Value rValue) { - return LongValue.create(toLong() ^ rValue.toLong()); - } - - /** - * Absolute value. - */ - public Value abs() { - if (getValueType().isDoubleCmp()) { - return new DoubleValue(Math.abs(toDouble())); - } else { - return LongValue.create(Math.abs(toLong())); - } - } - - /** - * Returns the next array index based on this value. - */ - public long nextIndex(long oldIndex) { - return oldIndex; - } - - // - // string functions - // - /** - * Returns the length as a string. - */ - public int length() { - return toStringValue().length(); - } - - // - // Array functions - // - /** - * Returns the array size. - */ - public int getSize() { - return 1; - } - - /** - * Returns the count, as returned by the global php count() function - */ - public int getCount(Env env) { - return 1; - } - - /** - * Returns the count, as returned by the global php count() function - */ - public int getCountRecursive(Env env) { - return getCount(env); - } - - /** - * Returns an iterator for the key => value pairs. - */ - public Iterator> getIterator(Env env) { - return getBaseIterator(env); - } - - /** - * Returns an iterator for the key => value pairs. - */ - public Iterator> getBaseIterator(Env env) { - Set> emptySet = Collections.emptySet(); - - return emptySet.iterator(); - } - - /** - * Returns an iterator for the field keys. - * The default implementation uses the Iterator returned - * by {@link #getIterator(Env)}; derived classes may override and - * provide a more efficient implementation. - */ - public Iterator getKeyIterator(Env env) { - final Iterator> iter = getIterator(env); - - return new Iterator() { - - @Override - public boolean hasNext() { - return iter.hasNext(); - } - - @Override - public Value next() { - return iter.next().getKey(); - } - - @Override - public void remove() { - iter.remove(); - } - }; - } - - /** - * Returns the field keys. - */ - public Value[] getKeyArray(Env env) { - return NULL_VALUE_ARRAY; - } - - /** - * Returns the field values. - */ - public Value[] getValueArray(Env env) { - return NULL_VALUE_ARRAY; - } - - /** - * Returns an iterator for the field values. - * The default implementation uses the Iterator returned - * by {@link #getIterator(Env)}; derived classes may override and - * provide a more efficient implementation. - */ - public Iterator getValueIterator(Env env) { - final Iterator> iter = getIterator(env); - - return new Iterator() { - - @Override - public boolean hasNext() { - return iter.hasNext(); - } - - @Override - public Value next() { - return iter.next().getValue(); - } - - @Override - public void remove() { - iter.remove(); - } - }; - } - - // - // Object field references - // - /** - * Returns the field value - */ - public Value getField(Env env, StringValue name) { - return NullValue.NULL; - } - - /** - * Returns the field ref. - */ - public Var getFieldVar(Env env, StringValue name) { - return getField(env, name).toVar(); - } - - /** - * Returns the field used as a method argument - */ - public Value getFieldArg(Env env, StringValue name, boolean isTop) { - return getFieldVar(env, name); - } - - /** - * Returns the field ref for an argument. - */ - public Value getFieldArgRef(Env env, StringValue name) { - return getFieldVar(env, name); - } - - /** - * Returns the value for a field, creating an object if the field - * is unset. - */ - public Value getFieldObject(Env env, StringValue name) { - Value v = getField(env, name); - - if (!v.isset()) { - v = env.createObject(); - - putField(env, name, v); - } - - return v; - } - - /** - * Returns the value for a field, creating an object if the field - * is unset. - */ - public Value getFieldArray(Env env, StringValue name) { - Value v = getField(env, name); - - Value array = v.toAutoArray(); - - if (v == array) { - return v; - } else { - putField(env, name, array); - - return array; - } - } - - /** - * Returns the field ref. - */ - public Value putField(Env env, StringValue name, Value object) { - return NullValue.NULL; - } - - public final Value putField(Env env, StringValue name, Value value, - Value innerIndex, Value innerValue) { - Value result = value.append(innerIndex, innerValue); - - return putField(env, name, result); - } - - public void setFieldInit(boolean isInit) { - } - - /** - * Returns true if the object is in a __set() method call. - * Prevents infinite recursion. - */ - public boolean isFieldInit() { - return false; - } - - /** - * Returns true if the field is set - */ - public boolean issetField(StringValue name) { - return false; - } - - /** - * Removes the field ref. - */ - public void unsetField(StringValue name) { - } - - /** - * Removes the field ref. - */ - public void unsetArray(Env env, StringValue name, Value index) { - } - - /** - * Removes the field ref. - */ - public void unsetThisArray(Env env, StringValue name, Value index) { - } - - /** - * Returns the field as a Var or Value. - */ - public Value getThisField(Env env, StringValue name) { - return getField(env, name); - } - - /** - * Returns the field as a Var. - */ - public Var getThisFieldVar(Env env, StringValue name) { - return getThisField(env, name).toVar(); - } - - /** - * Returns the field used as a method argument - */ - public Value getThisFieldArg(Env env, StringValue name) { - return getThisFieldVar(env, name); - } - - /** - * Returns the field ref for an argument. - */ - public Value getThisFieldArgRef(Env env, StringValue name) { - return getThisFieldVar(env, name); - } - - /** - * Returns the value for a field, creating an object if the field - * is unset. - */ - public Value getThisFieldObject(Env env, StringValue name) { - Value v = getThisField(env, name); - - if (!v.isset()) { - v = env.createObject(); - - putThisField(env, name, v); - } - - return v; - } - - /** - * Returns the value for a field, creating an object if the field - * is unset. - */ - public Value getThisFieldArray(Env env, StringValue name) { - Value v = getThisField(env, name); - - Value array = v.toAutoArray(); - - if (v == array) { - return v; - } else { - putField(env, name, array); - - return array; - } - } - - /** - * Initializes a new field, does not call __set if it is defined. - */ - public void initField(StringValue key, - Value value, - FieldVisibility visibility) { - putThisField(Env.getInstance(), key, value); - } - - /** - * Returns the field ref. - */ - public Value putThisField(Env env, StringValue name, Value object) { - return putField(env, name, object); - } - - /** - * Sets an array field ref. - */ - public Value putThisField(Env env, - StringValue name, - Value array, - Value index, - Value value) { - Value result = array.append(index, value); - - putThisField(env, name, result); - - return value; - } - - /** - * Returns true if the field is set - */ - public boolean issetThisField(StringValue name) { - return issetField(name); - } - - /** - * Removes the field ref. - */ - public void unsetThisField(StringValue name) { - unsetField(name); - } - - // - // field convenience - // - public Value putField(Env env, String name, Value value) { - return putThisField(env, env.createString(name), value); - } - - /** - * Returns the array ref. - */ - public Value get(Value index) { - return UnsetValue.UNSET; - } - - /** - * Returns a reference to the array value. - */ - public Var getVar(Value index) { - Value value = get(index); - - if (value.isVar()) { - return (Var) value; - } else { - return new Var(value); - } - } - - /** - * Returns a reference to the array value. - */ - public Value getRef(Value index) { - return get(index); - } - - /** - * Returns the array ref as a function argument. - */ - public Value getArg(Value index, boolean isTop) { - return get(index); - } - - /** - * Returns the array value, copying on write if necessary. - */ - public Value getDirty(Value index) { - return get(index); - } - - /** - * Returns the value for a field, creating an array if the field - * is unset. - */ - public Value getArray() { - return this; - } - - /** - * Returns the value for a field, creating an array if the field - * is unset. - */ - public Value getArray(Value index) { - Value var = getVar(index); - - return var.toAutoArray(); - } - - /** - * Returns the value for the variable, creating an object if the var - * is unset. - */ - public Value getObject(Env env) { - return NullValue.NULL; - } - - /** - * Returns the value for a field, creating an object if the field - * is unset. - */ - public Value getObject(Env env, Value index) { - Value var = getVar(index); - - if (var.isset()) { - return var.toValue(); - } else { - var.set(env.createObject()); - - return var.toValue(); - } - } - - public boolean isVar() { - return false; - } - - /** - * Sets the value ref. - */ - public Value set(Value value) { - return value; - } - - /** - * Sets the array ref and returns the value - */ - public Value put(Value index, Value value) { - Env.getCurrent().warning(L.l("{0} cannot be used as an array", - toDebugString())); - - return value; - } - - /** - * Sets the array ref. - */ - public final Value put(Value index, Value value, - Value innerIndex, Value innerValue) { - Value result = value.append(innerIndex, innerValue); - - put(index, result); - - return innerValue; - } - - /** - * Appends an array value - */ - public Value put(Value value) { - /* - Env.getCurrent().warning(L.l("{0} cannot be used as an array", - toDebugString())); - */ - - - return value; - } - - /** - * Sets the array value, returning the new array, e.g. to handle - * string update ($a[0] = 'A'). Creates an array automatically if - * necessary. - */ - public Value append(Value index, Value value) { - Value array = toAutoArray(); - - if (array.isArray()) { - return array.append(index, value); - } else { - return array; - } - } - - /** - * Sets the array tail, returning the Var of the tail. - */ - public Var putVar() { - return new Var(); - } - - /** - * Appends a new object - */ - public Value putObject(Env env) { - Value value = env.createObject(); - - put(value); - - return value; - } - - /** - * Return true if the array value is set - */ - public boolean isset(Value index) { - return false; - } - - /** - * Returns true if the key exists in the array. - */ - public boolean keyExists(Value key) { - return isset(key); - } - - /** - * Returns the corresponding value if this array contains the given key - * - * @param key to search for in the array - * - * @return the value if it is found in the array, NULL otherwise - */ - public Value containsKey(Value key) { - return null; - } - - /** - * Return unset the value. - */ - public Value remove(Value index) { - return UnsetValue.UNSET; - } - - /** - * Takes the values of this array, unmarshalls them to objects of type - * elementType, and puts them in a java array. - */ - public Object valuesToArray(Env env, Class elementType) { - env.error(L.l("Can't assign {0} with type {1} to {2}[]", - this, - this.getClass(), - elementType)); - return null; - } - - /** - * Returns the character at the named index. - */ - public Value charValueAt(long index) { - return NullValue.NULL; - } - - /** - * Sets the character at the named index. - */ - public Value setCharValueAt(long index, Value value) { - return NullValue.NULL; - } - - /** - * Prints the value. - * @param env - */ - public void print(Env env) { - env.print(toString(env)); - } - - /** - * Prints the value. - * @param env - */ - public void print(Env env, WriteStream out) { - try { - out.print(toString(env)); - } catch (IOException e) { - throw new QuercusRuntimeException(e); - } - } - - /** - * Serializes the value. - * - * @param env - * @param sb holds result of serialization - * @param serializeMap holds reference indexes - */ - public void serialize(Env env, - StringBuilder sb, - SerializeMap serializeMap) { - serializeMap.incrementIndex(); - - serialize(env, sb); - } - - /** - * Encodes the value in JSON. - */ - public void jsonEncode(Env env, StringValue sb) { - env.warning(L.l("type is unsupported; json encoded as null")); - - sb.append("null"); - } - - /** - * Serializes the value. - */ - public void serialize(Env env, StringBuilder sb) { - throw new UnsupportedOperationException(getClass().getName()); - } - - /** - * Exports the value. - */ - public void varExport(StringBuilder sb) { - throw new UnsupportedOperationException(getClass().getName()); - } - - /** - * Binds a Java object to this object. - */ - public void setJavaObject(Value value) { - } - - // - // Java generator code - // - /** - * Generates code to recreate the expression. - * - * @param out the writer to the Java source code. - */ - public void generate(PrintWriter out) - throws IOException { - } - - protected static void printJavaChar(PrintWriter out, char ch) { - switch (ch) { - case '\r': - out.print("\\r"); - break; - case '\n': - out.print("\\n"); - break; - //case '\"': - // out.print("\\\""); - // break; - case '\'': - out.print("\\\'"); - break; - case '\\': - out.print("\\\\"); - break; - default: - out.print(ch); - break; - } - } - - protected static void printJavaString(PrintWriter out, StringValue s) { - if (s == null) { - out.print(""); - return; - } - - int len = s.length(); - for (int i = 0; i < len; i++) { - char ch = s.charAt(i); - - switch (ch) { + } + } + + /** + * Multiplies to the following value. + */ + public Value div(long r) { + long l = toLong(); + + if (r != 0 && l % r == 0) { + return LongValue.create(l / r); + } else { + return new DoubleValue(toDouble() / r); + } + } + + /** + * modulo the following value. + */ + public Value mod(Value rValue) { + double lDouble = toDouble(); + double rDouble = rValue.toDouble(); + + return LongValue.create((long) lDouble % rDouble); + } + + /** + * Shifts left by the value. + */ + public Value lshift(Value rValue) { + long lLong = toLong(); + long rLong = rValue.toLong(); + + return LongValue.create(lLong << rLong); + } + + /** + * Shifts right by the value. + */ + public Value rshift(Value rValue) { + long lLong = toLong(); + long rLong = rValue.toLong(); + + return LongValue.create(lLong >> rLong); + } + + /* + * Binary And. + */ + public Value bitAnd(Value rValue) { + return LongValue.create(toLong() & rValue.toLong()); + } + + /* + * Binary or. + */ + public Value bitOr(Value rValue) { + return LongValue.create(toLong() | rValue.toLong()); + } + + /** + * Binary xor. + */ + public Value bitXor(Value rValue) { + return LongValue.create(toLong() ^ rValue.toLong()); + } + + /** + * Absolute value. + */ + public Value abs() { + if (getValueType().isDoubleCmp()) { + return new DoubleValue(Math.abs(toDouble())); + } else { + return LongValue.create(Math.abs(toLong())); + } + } + + /** + * Returns the next array index based on this value. + */ + public long nextIndex(long oldIndex) { + return oldIndex; + } + + // + // string functions + // + /** + * Returns the length as a string. + */ + public int length() { + return toStringValue().length(); + } + + // + // Array functions + // + /** + * Returns the array size. + */ + public int getSize() { + return 1; + } + + /** + * Returns the count, as returned by the global php count() function + */ + public int getCount(Env env) { + return 1; + } + + /** + * Returns the count, as returned by the global php count() function + */ + public int getCountRecursive(Env env) { + return getCount(env); + } + + /** + * Returns an iterator for the key => value pairs. + */ + public Iterator> getIterator(Env env) { + return getBaseIterator(env); + } + + /** + * Returns an iterator for the key => value pairs. + */ + public Iterator> getBaseIterator(Env env) { + Set> emptySet = Collections.emptySet(); + + return emptySet.iterator(); + } + + /** + * Returns an iterator for the field keys. + * The default implementation uses the Iterator returned + * by {@link #getIterator(Env)}; derived classes may override and + * provide a more efficient implementation. + */ + public Iterator getKeyIterator(Env env) { + final Iterator> iter = getIterator(env); + + return new Iterator() { + + @Override + public boolean hasNext() { + return iter.hasNext(); + } + + @Override + public Value next() { + return iter.next().getKey(); + } + + @Override + public void remove() { + iter.remove(); + } + }; + } + + /** + * Returns the field keys. + */ + public Value[] getKeyArray(Env env) { + return NULL_VALUE_ARRAY; + } + + /** + * Returns the field values. + */ + public Value[] getValueArray(Env env) { + return NULL_VALUE_ARRAY; + } + + /** + * Returns an iterator for the field values. + * The default implementation uses the Iterator returned + * by {@link #getIterator(Env)}; derived classes may override and + * provide a more efficient implementation. + */ + public Iterator getValueIterator(Env env) { + final Iterator> iter = getIterator(env); + + return new Iterator() { + + @Override + public boolean hasNext() { + return iter.hasNext(); + } + + @Override + public Value next() { + return iter.next().getValue(); + } + + @Override + public void remove() { + iter.remove(); + } + }; + } + + // + // Object field references + // + /** + * Returns the field value + */ + public Value getField(Env env, StringValue name) { + return NullValue.NULL; + } + + /** + * Returns the field ref. + */ + public Var getFieldVar(Env env, StringValue name) { + return getField(env, name).toVar(); + } + + /** + * Returns the field used as a method argument + */ + public Value getFieldArg(Env env, StringValue name, boolean isTop) { + return getFieldVar(env, name); + } + + /** + * Returns the field ref for an argument. + */ + public Value getFieldArgRef(Env env, StringValue name) { + return getFieldVar(env, name); + } + + /** + * Returns the value for a field, creating an object if the field + * is unset. + */ + public Value getFieldObject(Env env, StringValue name) { + Value v = getField(env, name); + + if (!v.isset()) { + v = env.createObject(); + + putField(env, name, v); + } + + return v; + } + + /** + * Returns the value for a field, creating an object if the field + * is unset. + */ + public Value getFieldArray(Env env, StringValue name) { + Value v = getField(env, name); + + Value array = v.toAutoArray(); + + if (v == array) { + return v; + } else { + putField(env, name, array); + + return array; + } + } + + /** + * Returns the field ref. + */ + public Value putField(Env env, StringValue name, Value object) { + return NullValue.NULL; + } + + public final Value putField(Env env, StringValue name, Value value, + Value innerIndex, Value innerValue) { + Value result = value.append(innerIndex, innerValue); + + return putField(env, name, result); + } + + public void setFieldInit(boolean isInit) { + } + + /** + * Returns true if the object is in a __set() method call. + * Prevents infinite recursion. + */ + public boolean isFieldInit() { + return false; + } + + /** + * Returns true if the field is set + */ + public boolean issetField(StringValue name) { + return false; + } + + /** + * Removes the field ref. + */ + public void unsetField(StringValue name) { + } + + /** + * Removes the field ref. + */ + public void unsetArray(Env env, StringValue name, Value index) { + } + + /** + * Removes the field ref. + */ + public void unsetThisArray(Env env, StringValue name, Value index) { + } + + /** + * Returns the field as a Var or Value. + */ + public Value getThisField(Env env, StringValue name) { + return getField(env, name); + } + + /** + * Returns the field as a Var. + */ + public Var getThisFieldVar(Env env, StringValue name) { + return getThisField(env, name).toVar(); + } + + /** + * Returns the field used as a method argument + */ + public Value getThisFieldArg(Env env, StringValue name) { + return getThisFieldVar(env, name); + } + + /** + * Returns the field ref for an argument. + */ + public Value getThisFieldArgRef(Env env, StringValue name) { + return getThisFieldVar(env, name); + } + + /** + * Returns the value for a field, creating an object if the field + * is unset. + */ + public Value getThisFieldObject(Env env, StringValue name) { + Value v = getThisField(env, name); + + if (!v.isset()) { + v = env.createObject(); + + putThisField(env, name, v); + } + + return v; + } + + /** + * Returns the value for a field, creating an object if the field + * is unset. + */ + public Value getThisFieldArray(Env env, StringValue name) { + Value v = getThisField(env, name); + + Value array = v.toAutoArray(); + + if (v == array) { + return v; + } else { + putField(env, name, array); + + return array; + } + } + + /** + * Initializes a new field, does not call __set if it is defined. + */ + public void initField(StringValue key, + Value value, + FieldVisibility visibility) { + putThisField(Env.getInstance(), key, value); + } + + /** + * Returns the field ref. + */ + public Value putThisField(Env env, StringValue name, Value object) { + return putField(env, name, object); + } + + /** + * Sets an array field ref. + */ + public Value putThisField(Env env, + StringValue name, + Value array, + Value index, + Value value) { + Value result = array.append(index, value); + + putThisField(env, name, result); + + return value; + } + + /** + * Returns true if the field is set + */ + public boolean issetThisField(StringValue name) { + return issetField(name); + } + + /** + * Removes the field ref. + */ + public void unsetThisField(StringValue name) { + unsetField(name); + } + + // + // field convenience + // + public Value putField(Env env, String name, Value value) { + return putThisField(env, env.createString(name), value); + } + + /** + * Returns the array ref. + */ + public Value get(Value index) { + return UnsetValue.UNSET; + } + + /** + * Returns a reference to the array value. + */ + public Var getVar(Value index) { + Value value = get(index); + + if (value.isVar()) { + return (Var) value; + } else { + return new Var(value); + } + } + + /** + * Returns a reference to the array value. + */ + public Value getRef(Value index) { + return get(index); + } + + /** + * Returns the array ref as a function argument. + */ + public Value getArg(Value index, boolean isTop) { + return get(index); + } + + /** + * Returns the array value, copying on write if necessary. + */ + public Value getDirty(Value index) { + return get(index); + } + + /** + * Returns the value for a field, creating an array if the field + * is unset. + */ + public Value getArray() { + return this; + } + + /** + * Returns the value for a field, creating an array if the field + * is unset. + */ + public Value getArray(Value index) { + Value var = getVar(index); + + return var.toAutoArray(); + } + + /** + * Returns the value for the variable, creating an object if the var + * is unset. + */ + public Value getObject(Env env) { + return NullValue.NULL; + } + + /** + * Returns the value for a field, creating an object if the field + * is unset. + */ + public Value getObject(Env env, Value index) { + Value var = getVar(index); + + if (var.isset()) { + return var.toValue(); + } else { + var.set(env.createObject()); + + return var.toValue(); + } + } + + public boolean isVar() { + return false; + } + + /** + * Sets the value ref. + */ + public Value set(Value value) { + return value; + } + + /** + * Sets the array ref and returns the value + */ + public Value put(Value index, Value value) { + Env.getCurrent().warning(L.l("{0} cannot be used as an array", + toDebugString())); + + return value; + } + + /** + * Sets the array ref. + */ + public final Value put(Value index, Value value, + Value innerIndex, Value innerValue) { + Value result = value.append(innerIndex, innerValue); + + put(index, result); + + return innerValue; + } + + /** + * Appends an array value + */ + public Value put(Value value) { + /* + Env.getCurrent().warning(L.l("{0} cannot be used as an array", + toDebugString())); + */ + + + return value; + } + + /** + * Sets the array value, returning the new array, e.g. to handle + * string update ($a[0] = 'A'). Creates an array automatically if + * necessary. + */ + public Value append(Value index, Value value) { + Value array = toAutoArray(); + + if (array.isArray()) { + return array.append(index, value); + } else { + return array; + } + } + + /** + * Sets the array tail, returning the Var of the tail. + */ + public Var putVar() { + return new Var(); + } + + /** + * Appends a new object + */ + public Value putObject(Env env) { + Value value = env.createObject(); + + put(value); + + return value; + } + + /** + * Return true if the array value is set + */ + public boolean isset(Value index) { + return false; + } + + /** + * Returns true if the key exists in the array. + */ + public boolean keyExists(Value key) { + return isset(key); + } + + /** + * Returns the corresponding value if this array contains the given key + * + * @param key to search for in the array + * + * @return the value if it is found in the array, NULL otherwise + */ + public Value containsKey(Value key) { + return null; + } + + /** + * Return unset the value. + */ + public Value remove(Value index) { + return UnsetValue.UNSET; + } + + /** + * Takes the values of this array, unmarshalls them to objects of type + * elementType, and puts them in a java array. + */ + public Object valuesToArray(Env env, Class elementType) { + env.error(L.l("Can't assign {0} with type {1} to {2}[]", + this, + this.getClass(), + elementType)); + return null; + } + + /** + * Returns the character at the named index. + */ + public Value charValueAt(long index) { + return NullValue.NULL; + } + + /** + * Sets the character at the named index. + */ + public Value setCharValueAt(long index, Value value) { + return NullValue.NULL; + } + + /** + * Prints the value. + * @param env + */ + public void print(Env env) { + env.print(toString(env)); + } + + /** + * Prints the value. + * @param env + */ + public void print(Env env, WriteStream out) { + try { + out.print(toString(env)); + } catch (IOException e) { + throw new QuercusRuntimeException(e); + } + } + + /** + * Serializes the value. + * + * @param env + * @param sb holds result of serialization + * @param serializeMap holds reference indexes + */ + public void serialize(Env env, + StringBuilder sb, + SerializeMap serializeMap) { + serializeMap.incrementIndex(); + + serialize(env, sb); + } + + /** + * Encodes the value in JSON. + */ + public void jsonEncode(Env env, StringValue sb) { + env.warning(L.l("type is unsupported; json encoded as null")); + + sb.append("null"); + } + + /** + * Serializes the value. + */ + public void serialize(Env env, StringBuilder sb) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Exports the value. + */ + public void varExport(StringBuilder sb) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Binds a Java object to this object. + */ + public void setJavaObject(Value value) { + } + + // + // Java generator code + // + /** + * Generates code to recreate the expression. + * + * @param out the writer to the Java source code. + */ + public void generate(PrintWriter out) + throws IOException { + } + + protected static void printJavaChar(PrintWriter out, char ch) { + switch (ch) { case '\r': - out.print("\\r"); - break; + out.print("\\r"); + break; case '\n': - out.print("\\n"); - break; - case '\"': - out.print("\\\""); - break; + out.print("\\n"); + break; + //case '\"': + // out.print("\\\""); + // break; case '\'': - out.print("\\\'"); - break; + out.print("\\\'"); + break; case '\\': - out.print("\\\\"); - break; + out.print("\\\\"); + break; default: - out.print(ch); - break; - } - } - } - - public String toInternString() { - return toString().intern(); - } - - public String toDebugString() { - return toString(); - } - - public final void varDump(Env env, - WriteStream out, - int depth, - IdentityHashMap valueSet) - throws IOException { - if (valueSet.get(this) != null) { - out.print("*recursion*"); - return; - } - - valueSet.put(this, "printing"); - - try { - varDumpImpl(env, out, depth, valueSet); - } finally { - valueSet.remove(this); - } - } - - protected void varDumpImpl(Env env, - WriteStream out, - int depth, - IdentityHashMap valueSet) - throws IOException { - out.print("resource(" + toString() + ")"); - } - - public final void printR(Env env, - WriteStream out, - int depth, - IdentityHashMap valueSet) - throws IOException { - if (valueSet.get(this) != null) { - out.print("*recursion*"); - return; - } - - valueSet.put(this, "printing"); - - try { - printRImpl(env, out, depth, valueSet); - } finally { - valueSet.remove(this); - } - } - - protected void printRImpl(Env env, - WriteStream out, - int depth, - IdentityHashMap valueSet) - throws IOException { - out.print(toString()); - } - - protected void printDepth(WriteStream out, int depth) - throws IOException { - for (int i = 0; i < depth; i++) { - out.print(' '); - } - } - - public int getHashCode() { - return hashCode(); - } - - @Override - public int hashCode() { - return 1021; - } + out.print(ch); + break; + } + } + + protected static void printJavaString(PrintWriter out, StringValue s) { + if (s == null) { + out.print(""); + return; + } + + int len = s.length(); + for (int i = 0; i < len; i++) { + char ch = s.charAt(i); + + switch (ch) { + case '\r': + out.print("\\r"); + break; + case '\n': + out.print("\\n"); + break; + case '\"': + out.print("\\\""); + break; + case '\'': + out.print("\\\'"); + break; + case '\\': + out.print("\\\\"); + break; + default: + out.print(ch); + break; + } + } + } + + public String toInternString() { + return toString().intern(); + } + + public String toDebugString() { + return toString(); + } + + public final void varDump(Env env, + WriteStream out, + int depth, + IdentityHashMap valueSet) + throws IOException { + if (valueSet.get(this) != null) { + out.print("*recursion*"); + return; + } + + valueSet.put(this, "printing"); + + try { + varDumpImpl(env, out, depth, valueSet); + } finally { + valueSet.remove(this); + } + } + + protected void varDumpImpl(Env env, + WriteStream out, + int depth, + IdentityHashMap valueSet) + throws IOException { + out.print("resource(" + toString() + ")"); + } + + public final void printR(Env env, + WriteStream out, + int depth, + IdentityHashMap valueSet) + throws IOException { + if (valueSet.get(this) != null) { + out.print("*recursion*"); + return; + } + + valueSet.put(this, "printing"); + + try { + printRImpl(env, out, depth, valueSet); + } finally { + valueSet.remove(this); + } + } + + protected void printRImpl(Env env, + WriteStream out, + int depth, + IdentityHashMap valueSet) + throws IOException { + out.print(toString()); + } + + protected void printDepth(WriteStream out, int depth) + throws IOException { + for (int i = 0; i < depth; i++) { + out.print(' '); + } + } + + public int getHashCode() { + return hashCode(); + } + + @Override + public int hashCode() { + return 1021; + } } diff --git a/quercus/src/main/java/com/caucho/quercus/lib/db/JdbcConnectionResource.java b/quercus/src/main/java/com/caucho/quercus/lib/db/JdbcConnectionResource.java index 333a32d..f33d9f8 100644 --- a/quercus/src/main/java/com/caucho/quercus/lib/db/JdbcConnectionResource.java +++ b/quercus/src/main/java/com/caucho/quercus/lib/db/JdbcConnectionResource.java @@ -28,6 +28,11 @@ */ package com.caucho.quercus.lib.db; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.sql.DataSource; + import com.caucho.quercus.env.BooleanValue; import com.caucho.quercus.env.ConnectionEntry; import com.caucho.quercus.env.Env; @@ -48,1131 +53,1185 @@ public abstract class JdbcConnectionResource implements EnvCleanup { - private static final L10N L = new L10N(JdbcConnectionResource.class); - private static final Logger log = Logger.getLogger(JdbcConnectionResource.class.getName()); - private static LruCache _tableMetadataMap = new LruCache(256); - protected ConnectionEntry _conn; - // cached statement - private Statement _savedStmt; - private Statement _freeStmt; - private DatabaseMetaData _dmd; - private JdbcResultResource _rs; - private int _affectedRows; - private String _errorMessage = null; - private int _errorCode; - private SQLWarning _warnings; - private Env _env; - protected String _host; - protected int _port; - private String _userName; - private String _password; - protected String _driver; - protected String _url; - protected int _flags; - protected String _socket; - private String _catalog; - private boolean _isCatalogOptimEnabled = false; - private boolean _isUsed; - protected SqlParseToken _sqlParseToken = new SqlParseToken(); - protected static String ENCODING; - - public JdbcConnectionResource(Env env) { - _env = env; - - ENCODING = env.getQuercus().getJdbcEncoding(); - - env.addCleanup(this); - } - - /** - * Returns the error string for the most recent function call. - * This method is not invoked from PHP code. - */ - public StringValue error(Env env) { - if (isConnected()) { - return env.createString(getErrorMessage()); - } else { - return env.getEmptyString(); - } - } - - public boolean isConnected() { - return _conn != null; - } - - public Env getEnv() { - return _env; - } - - public String getHost() { - return _host; - } - - public String getUserName() { - return _userName; - } - - public String getPassword() { - return _password; - } - - public String getDbName() { - return _catalog; - } - - public int getPort() { - return _port; - } - - public String getDriver() { - return _driver; - } - - public String getUrl() { - return _url; - } - - /** - * Set the current underlying connection and - * corresponding information: host, port and - * database name. - * - * @param host server host - * @param port server port - * @param dbname database name - */ - final protected boolean connectInternal(Env env, - String host, - String userName, - String password, - String dbname, - int port, - String socket, - int flags, - String driver, - String url, - boolean isNewLink) { - if (_conn != null) { - throw new IllegalStateException( - getClass().getSimpleName() + " attempt to open multiple connections"); - } - - _host = host; - _userName = userName; - _password = password; - _port = port; - _socket = socket; - _flags = flags; - _driver = driver; - _url = url; - - if (dbname == null) { - dbname = ""; - } - - _catalog = dbname; - - _conn = connectImpl(env, host, userName, password, - dbname, port, socket, flags, driver, url, - isNewLink); - - if (_conn != null) { - try { - if ("".equals(_catalog)) { - _catalog = _conn.getConnection().getCatalog(); + private static final L10N L = new L10N(JdbcConnectionResource.class); + private static final Logger log = Logger.getLogger(JdbcConnectionResource.class.getName()); + private static LruCache _tableMetadataMap = new LruCache(256); + protected ConnectionEntry _conn; + // cached statement + private Statement _savedStmt; + private Statement _freeStmt; + private DatabaseMetaData _dmd; + private JdbcResultResource _rs; + private int _affectedRows; + private String _errorMessage = null; + private int _errorCode; + private SQLWarning _warnings; + private Env _env; + protected String _host; + protected int _port; + private String _userName; + private String _password; + protected String _driver; + protected String _url; + protected int _flags; + protected String _socket; + private String _catalog; + private boolean _isCatalogOptimEnabled = false; + private boolean _isUsed; + protected SqlParseToken _sqlParseToken = new SqlParseToken(); + protected static String ENCODING; + + public JdbcConnectionResource(Env env) { + _env = env; + + ENCODING = env.getQuercus().getJdbcEncoding(); + + env.addCleanup(this); + } + + /** + * Returns the error string for the most recent function call. + * This method is not invoked from PHP code. + */ + public StringValue error(Env env) { + if (isConnected()) { + return env.createString(getErrorMessage()); + } else { + return env.getEmptyString(); + } + } + + public boolean isConnected() { + return _conn != null; + } + + public Env getEnv() { + return _env; + } + + public String getHost() { + return _host; + } + + public String getUserName() { + return _userName; + } + + public String getPassword() { + return _password; + } + + public String getDbName() { + return _catalog; + } + + public int getPort() { + return _port; + } + + public String getDriver() { + return _driver; + } + + public String getUrl() { + return _url; + } + + /** + * Set the current underlying connection and + * corresponding information: host, port and + * database name. + * + * @param host server host + * @param port server port + * @param dbname database name + */ + final protected boolean connectInternal(Env env, + String host, + String userName, + String password, + String dbname, + int port, + String socket, + int flags, + String driver, + String url, + boolean isNewLink) { + if (_conn != null) { + throw new IllegalStateException( + getClass().getSimpleName() + " attempt to open multiple connections"); + } + + _host = host; + _userName = userName; + _password = password; + _port = port; + _socket = socket; + _flags = flags; + _driver = driver; + _url = url; + + if (dbname == null) { + dbname = ""; + } + + _catalog = dbname; + + _conn = connectImpl(env, host, userName, password, + dbname, port, socket, flags, driver, url, + isNewLink); + + if (_conn != null) { + try { + if ("".equals(_catalog)) { + _catalog = _conn.getConnection().getCatalog(); + } + } catch (SQLException e) { + log.log(Level.FINE, e.toString(), e); } - } catch (SQLException e) { + } + + return _conn != null && _conn.getConnection() != null; + } + + /** + * Opens a connection based on the dsn. + */ + private DataSource getJndiDataSource(Env env, String dsn) { + DataSource ds = null; + + try { + Context ic = new InitialContext(); + + ds = (DataSource) ic.lookup(dsn); + } catch (NamingException e) { log.log(Level.FINE, e.toString(), e); - } - } - - return _conn != null && _conn.getConnection() != null; - } - - /** - * Connects to the underlying database. - */ - protected abstract ConnectionEntry connectImpl(Env env, - String host, - String userName, - String password, - String dbname, - int port, - String socket, - int flags, - String driver, - String url, - boolean isNewLink); - - /** - * Escape the given string for SQL statements. - * - * @param str a string - * @return the string escaped for SQL statements - */ - protected StringValue realEscapeString(StringValue str) { - StringValue buf = _env.createUnicodeBuilder(); - - final int strLength = str.length(); - - for (int i = 0; i < strLength; i++) { - char c = str.charAt(i); - - switch (c) { - case '\u0000': - buf.append('\\'); - buf.append(0); - break; - case '\n': - buf.append('\\'); - buf.append('n'); - break; - case '\r': - buf.append('\\'); - buf.append('r'); - break; - case '\\': - buf.append('\\'); - buf.append('\\'); - break; - case '\'': - buf.append('\\'); - buf.append('\''); - break; - case '"': - buf.append('\\'); - buf.append('\"'); - break; - case '\032': - buf.append('\\'); - buf.append('Z'); - break; - default: - buf.append(c); - break; - } - } - - return buf; - } - - /** - * Returns the affected rows from the last query. - */ - public int getAffectedRows() { - return _affectedRows; - } - - public void setAffectedRows(int i) { - _affectedRows = i; - } - - /** - * @return _fieldCount - */ - public int getFieldCount() { - if (_rs == null) { - return 0; - } else { - return _rs.getFieldCount(); - } - } - - /** - * Returns JdbcResultResource of available databases - */ - protected JdbcResultResource getCatalogs() { - clearErrors(); - - try { - if (_dmd == null) { - _dmd = _conn.getConnection().getMetaData(); - } + } + + if (ds == null) { + env.error(L.l("'{0}' is an unknown PDO JNDI data source.", dsn)); + } + + return ds; + } + + /** + * Set the current underlying connection and + * corresponding information: host, port and + * database name. JNDI CASE + * + * @param host server host + * @param port server port + * @param dbname database name + */ + final protected boolean connectInternalJNDI(Env env, String jndiPath) { + if (_conn != null) { + throw new IllegalStateException( + getClass().getSimpleName() + " attempt to open multiple connections"); + } + _conn = new ConnectionEntry(env); + _conn.init(getJndiDataSource(env, jndiPath), null, null); + try { + _conn.connect(true); + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + } + _host = ""; + _userName = ""; + _password = ""; + _port = 0; + _socket = ""; + _flags = 0; + _driver = ""; + _url = ""; + _catalog = ""; + return _conn != null && _conn.getConnection() != null; + } + + /** + * Connects to the underlying database. + */ + protected abstract ConnectionEntry connectImpl(Env env, + String host, + String userName, + String password, + String dbname, + int port, + String socket, + int flags, + String driver, + String url, + boolean isNewLink); + + /** + * Escape the given string for SQL statements. + * + * @param str a string + * @return the string escaped for SQL statements + */ + protected StringValue realEscapeString(StringValue str) { + StringValue buf = _env.createUnicodeBuilder(); + + final int strLength = str.length(); + + for (int i = 0; i < strLength; i++) { + char c = str.charAt(i); + + switch (c) { + case '\u0000': + buf.append('\\'); + buf.append(0); + break; + case '\n': + buf.append('\\'); + buf.append('n'); + break; + case '\r': + buf.append('\\'); + buf.append('r'); + break; + case '\\': + buf.append('\\'); + buf.append('\\'); + break; + case '\'': + buf.append('\\'); + buf.append('\''); + break; + case '"': + buf.append('\\'); + buf.append('\"'); + break; + case '\032': + buf.append('\\'); + buf.append('Z'); + break; + default: + buf.append(c); + break; + } + } + + return buf; + } + + /** + * Returns the affected rows from the last query. + */ + public int getAffectedRows() { + return _affectedRows; + } + + public void setAffectedRows(int i) { + _affectedRows = i; + } + + /** + * @return _fieldCount + */ + public int getFieldCount() { + if (_rs == null) { + return 0; + } else { + return _rs.getFieldCount(); + } + } + + /** + * Returns JdbcResultResource of available databases + */ + protected JdbcResultResource getCatalogs() { + clearErrors(); + + try { + if (_dmd == null) { + _dmd = _conn.getConnection().getMetaData(); + } - ResultSet rs = _dmd.getCatalogs(); + ResultSet rs = _dmd.getCatalogs(); - if (rs != null) { - return createResult(_env, _savedStmt, rs); - } else { + if (rs != null) { + return createResult(_env, _savedStmt, rs); + } else { + return null; + } + } catch (SQLException e) { + saveErrors(e); + log.log(Level.FINEST, e.toString(), e); return null; - } - } catch (SQLException e) { - saveErrors(e); - log.log(Level.FINEST, e.toString(), e); - return null; - } - } - - /** - * @return current catalog or false if error - */ - protected Value getCatalog() { - return _env.createString(_catalog); - } - - /** - * Returns the client encoding. - */ - public String getCharacterSetName() { - if (ENCODING.equals("ISO8859_1")) { - return "latin1"; - } - - return ENCODING; - } - - /** - * Alias for getCharacterSetName - */ - public String getClientEncoding() { - return getCharacterSetName(); - } - - /** - * Set encoding on the client side of the connection. - * Return true if the encoding was set, otherwise false. - */ - public boolean setClientEncoding(String encoding) { - return true; - } - - /** - * Returns the client version - * @deprecated - */ - public String getClientInfo() { - try { - if (_dmd == null) { - _dmd = _conn.getConnection().getMetaData(); - } - - return _dmd.getDatabaseProductVersion(); - } catch (SQLException e) { - log.log(Level.FINE, e.toString(), e); - return null; - } - } - - /** - * Returns the connection - */ - public final Connection getConnection(Env env) { - _isUsed = true; - - Connection conn = null; - - if (_conn != null) { - conn = _conn.getConnection(); - } - - if (conn != null) { - return conn; - } else if (_errorMessage != null) { - env.warning(_errorMessage); - return null; - } else { - env.warning(L.l("Connection is not available: {0}", _conn)); - - _errorMessage = L.l("Connection is not available: {0}", _conn); - - return null; - } - } - - /** - * Returns the unwrapped SQL connection - * associated to this statement. - */ - protected Connection getJavaConnection() - throws SQLException { - // TODO: jdbc for jdk 1.6 updates - return _env.getQuercus().getConnection(_conn.getConnection()); - } - - /** - * Returns the data source. - */ - public String getURL() { - // return getJavaConnection().getURL(); - return _url; - } - - /** - * Returns the last error code. - */ - public int getErrorCode() { - return _errorCode; - } - - /** - * Returns the last error message. - */ - public String getErrorMessage() { - return _errorMessage; - } - - /** - * - * returns the URL string for the given connection - * IE: jdbc:mysql://localhost:3306/test - * XXX: PHP returns Localhost via UNIX socket - */ - public String getHostInfo() - throws SQLException { - if (_dmd == null) { - _dmd = _conn.getConnection().getMetaData(); - } - - return _dmd.getURL(); - } - - /** - * returns the server version - */ - public String getServerInfo() - throws SQLException { - return getMetaData().getDatabaseProductVersion(); - } - - /** - * Returns the table metadata. - */ - public JdbcTableMetaData getTableMetaData(Env env, - String catalog, - String schema, - String table) - throws SQLException { - try { - if (table == null || table.equals("")) { + } + } + + /** + * @return current catalog or false if error + */ + protected Value getCatalog() { + return _env.createString(_catalog); + } + + /** + * Returns the client encoding. + */ + public String getCharacterSetName() { + if (ENCODING.equals("ISO8859_1")) { + return "latin1"; + } + + return ENCODING; + } + + /** + * Alias for getCharacterSetName + */ + public String getClientEncoding() { + return getCharacterSetName(); + } + + /** + * Set encoding on the client side of the connection. + * Return true if the encoding was set, otherwise false. + */ + public boolean setClientEncoding(String encoding) { + return true; + } + + /** + * Returns the client version + * @deprecated + */ + public String getClientInfo() { + try { + if (_dmd == null) { + _dmd = _conn.getConnection().getMetaData(); + } + + return _dmd.getDatabaseProductVersion(); + } catch (SQLException e) { + log.log(Level.FINE, e.toString(), e); return null; - } + } + } + + /** + * Returns the connection + */ + public final Connection getConnection(Env env) { + _isUsed = true; - TableKey key = new TableKey(getURL(), catalog, schema, table); + Connection conn = null; - // TODO: needs invalidation on DROP or ALTER - JdbcTableMetaData tableMd = _tableMetadataMap.get(key); + if (_conn != null) { + conn = _conn.getConnection(); + } + + if (conn != null) { + return conn; + } else if (_errorMessage != null) { + env.warning(_errorMessage); + return null; + } else { + env.warning(L.l("Connection is not available: {0}", _conn)); + + _errorMessage = L.l("Connection is not available: {0}", _conn); + + return null; + } + } + + /** + * Returns the unwrapped SQL connection + * associated to this statement. + */ + protected Connection getJavaConnection() + throws SQLException { + // TODO: jdbc for jdk 1.6 updates + return _env.getQuercus().getConnection(_conn.getConnection()); + } + + /** + * Returns the data source. + */ + public String getURL() { + // return getJavaConnection().getURL(); + return _url; + } + + /** + * Returns the last error code. + */ + public int getErrorCode() { + return _errorCode; + } + + /** + * Returns the last error message. + */ + public String getErrorMessage() { + return _errorMessage; + } + + /** + * + * returns the URL string for the given connection + * IE: jdbc:mysql://localhost:3306/test + * XXX: PHP returns Localhost via UNIX socket + */ + public String getHostInfo() + throws SQLException { + if (_dmd == null) { + _dmd = _conn.getConnection().getMetaData(); + } + + return _dmd.getURL(); + } + + /** + * returns the server version + */ + public String getServerInfo() + throws SQLException { + return getMetaData().getDatabaseProductVersion(); + } + + /** + * Returns the table metadata. + */ + public JdbcTableMetaData getTableMetaData(Env env, + String catalog, + String schema, + String table) + throws SQLException { + try { + if (table == null || table.equals("")) { + return null; + } + + TableKey key = new TableKey(getURL(), catalog, schema, table); + + // TODO: needs invalidation on DROP or ALTER + JdbcTableMetaData tableMd = _tableMetadataMap.get(key); + + if (tableMd != null && tableMd.isValid(env)) { + return tableMd; + } + + tableMd = new JdbcTableMetaData(env, + catalog, + schema, + table, + getMetaData()); + + _tableMetadataMap.put(key, tableMd); - if (tableMd != null && tableMd.isValid(env)) { return tableMd; - } - - tableMd = new JdbcTableMetaData(env, - catalog, - schema, - table, - getMetaData()); - - _tableMetadataMap.put(key, tableMd); - - return tableMd; - } catch (SQLException e) { - log.log(Level.FINE, e.toString(), e); - - return null; - } - } - - private DatabaseMetaData getMetaData() - throws SQLException { - if (_dmd == null) { - _dmd = _conn.getConnection().getMetaData(); - } - - return _dmd; - } - - static int infoToVersion(String info) { - String[] result = info.split("[.a-z-]"); - - if (result.length < 3) { - return 0; - } - - return (Integer.parseInt(result[0]) * 10000 - + Integer.parseInt(result[1]) * 100 - + Integer.parseInt(result[2])); - } - - public void closeStatement(Statement stmt) { - if (stmt == null) { - return; - } - - if (_freeStmt == null && false) { - _freeStmt = stmt; - } else { - JdbcUtil.close(stmt); - } - } - - /** - * Closes the connection. - */ - public boolean close(Env env) { - // php/1418 - // cleanup(); - - ConnectionEntry conn = _conn; - _conn = null; - - if (conn != null) { - conn.phpClose(); - } - - return true; - } - - /** - * Implements the EnvCleanup interface. This method - * will deallocate resources associated with this - * connection. This method can be invoked via a - * call to close(), or it can be invoked when the - * environment is being cleaned up after a quercus - * request has been processed. - */ - @Override - public void cleanup() { - if (log.isLoggable(Level.FINER)) { - log.finer(this + " cleanup()"); - } - - try { - Statement savedStmt = _savedStmt; - _savedStmt = null; - - Statement freeStmt = _freeStmt; - _freeStmt = null; - - if (savedStmt != null) { - savedStmt.close(); - } - - if (freeStmt != null) { - freeStmt.close(); - } - } catch (Throwable e) { - // must catch throwable to force the conn close to work - - log.log(Level.FINER, e.toString(), e); - } - - ConnectionEntry conn = _conn; - _conn = null; - - if (conn != null) { - conn.phpClose(); - } - } - - public JdbcConnectionResource validateConnection() { - if (_conn == null) { - throw _env.createErrorException( - L.l("Connection is not properly initialized {0}\nDriver {1}", - _url, _driver)); - } - - return this; - } - - /** - * Execute a single query. - */ - protected Value realQuery(Env env, String sql) { - clearErrors(); - - _rs = null; - - Statement stmt = _freeStmt; - _freeStmt = null; - - try { - Connection conn = getConnection(env); - - if (conn == null) { - return BooleanValue.FALSE; - } + } catch (SQLException e) { + log.log(Level.FINE, e.toString(), e); - if (checkSql(_conn, sql)) { - return BooleanValue.TRUE; - } + return null; + } + } - // statement reuse does not gain performance significantly (< 1%) - // php/142v - if (true || stmt == null) { - // TODO: test for performance + private DatabaseMetaData getMetaData() + throws SQLException { + if (_dmd == null) { + _dmd = _conn.getConnection().getMetaData(); + } + + return _dmd; + } + + static int infoToVersion(String info) { + String[] result = info.split("[.a-z-]"); + + if (result.length < 3) { + return 0; + } + + return (Integer.parseInt(result[0]) * 10000 + + Integer.parseInt(result[1]) * 100 + + Integer.parseInt(result[2])); + } + + public void closeStatement(Statement stmt) { + if (stmt == null) { + return; + } + + if (_freeStmt == null && false) { + _freeStmt = stmt; + } else { + JdbcUtil.close(stmt); + } + } + + /** + * Closes the connection. + */ + public boolean close(Env env) { + // php/1418 + // cleanup(); + + ConnectionEntry conn = _conn; + _conn = null; + + if (conn != null) { + conn.phpClose(); + } + + return true; + } + + /** + * Implements the EnvCleanup interface. This method + * will deallocate resources associated with this + * connection. This method can be invoked via a + * call to close(), or it can be invoked when the + * environment is being cleaned up after a quercus + * request has been processed. + */ + @Override + public void cleanup() { + if (log.isLoggable(Level.FINER)) { + log.finer(this + " cleanup()"); + } + + try { + Statement savedStmt = _savedStmt; + _savedStmt = null; + + Statement freeStmt = _freeStmt; + _freeStmt = null; + + if (savedStmt != null) { + savedStmt.close(); + } - boolean isSeekable = isSeekable(); - if (isSeekable) { - stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, - ResultSet.CONCUR_READ_ONLY); - } else { - stmt = conn.createStatement(); + if (freeStmt != null) { + freeStmt.close(); } + } catch (Throwable e) { + // must catch throwable to force the conn close to work - stmt.setEscapeProcessing(false); // php/1406 - } + log.log(Level.FINER, e.toString(), e); + } - if (stmt.execute(sql)) { - // Statement.execute(String) returns true when SQL statement is a - // SELECT statement that returns a result set. + ConnectionEntry conn = _conn; + _conn = null; - ResultSet rs = stmt.getResultSet(); - _rs = createResult(_env, stmt, rs); - _affectedRows = 0; + if (conn != null) { + conn.phpClose(); + } + } - // TODO: if these are needed, get them lazily for performance - // _warnings = stmt.getWarnings(); - } else { - // Statement.execute(String) returns false when SQL statement does - // not returns a result set (UPDATE, INSERT, DELETE, or REPLACE). + public JdbcConnectionResource validateConnection() { + if (_conn == null) { + throw _env.createErrorException( + L.l("Connection is not properly initialized {0}\nDriver {1}", + _url, _driver)); + } - // php/430a should return a result set - // for update statements. It is always - // null though. So keep the stmt for - // future reference (PostgresModule.pg_last_oid) + return this; + } - // php/1f33 + /** + * Execute a single query. + */ + protected Value realQuery(Env env, String sql) { + clearErrors(); - // This is overriden in Postgres.java - keepResourceValues(stmt); + _rs = null; - _affectedRows = 0; - _affectedRows = stmt.getUpdateCount(); - if (_rs != null) { - _rs.setAffectedRows(_affectedRows); + Statement stmt = _freeStmt; + _freeStmt = null; + + try { + Connection conn = getConnection(env); + + if (conn == null) { + return BooleanValue.FALSE; } - // TODO: if these are needed, get them lazily for performance - // _warnings = stmt.getWarnings(); + if (checkSql(_conn, sql)) { + return BooleanValue.TRUE; + } - // for php/430a - if (keepStatementOpen()) { - _savedStmt = stmt; + // statement reuse does not gain performance significantly (< 1%) + // php/142v + if (true || stmt == null) { + // TODO: test for performance + + boolean isSeekable = isSeekable(); + if (isSeekable) { + stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, + ResultSet.CONCUR_READ_ONLY); + } else { + stmt = conn.createStatement(); + } + + stmt.setEscapeProcessing(false); // php/1406 + } + + if (stmt.execute(sql)) { + // Statement.execute(String) returns true when SQL statement is a + // SELECT statement that returns a result set. + + ResultSet rs = stmt.getResultSet(); + _rs = createResult(_env, stmt, rs); + _affectedRows = 0; + + // TODO: if these are needed, get them lazily for performance + // _warnings = stmt.getWarnings(); } else { - // _warnings = stmt.getWarnings(); - _freeStmt = stmt; + // Statement.execute(String) returns false when SQL statement does + // not returns a result set (UPDATE, INSERT, DELETE, or REPLACE). + + // php/430a should return a result set + // for update statements. It is always + // null though. So keep the stmt for + // future reference (PostgresModule.pg_last_oid) + + // php/1f33 + + // This is overriden in Postgres.java + keepResourceValues(stmt); + + _affectedRows = 0; + _affectedRows = stmt.getUpdateCount(); + if (_rs != null) { + _rs.setAffectedRows(_affectedRows); + } + + // TODO: if these are needed, get them lazily for performance + // _warnings = stmt.getWarnings(); + + // for php/430a + if (keepStatementOpen()) { + _savedStmt = stmt; + } else { + // _warnings = stmt.getWarnings(); + _freeStmt = stmt; + } } - } - } catch (DataTruncation truncationError) { - try { - _affectedRows = stmt.getUpdateCount(); - //_warnings = stmt.getWarnings(); - } catch (SQLException e) { + } catch (DataTruncation truncationError) { + try { + _affectedRows = stmt.getUpdateCount(); + //_warnings = stmt.getWarnings(); + } catch (SQLException e) { + saveErrors(e); + log.log(Level.FINEST, e.toString(), e); + return BooleanValue.FALSE; + } + } catch (SQLException e) { saveErrors(e); - log.log(Level.FINEST, e.toString(), e); - return BooleanValue.FALSE; - } - } catch (SQLException e) { - saveErrors(e); - - // php/431h - if (keepStatementOpen()) { - keepResourceValues(stmt); - } else { - log.log(Level.FINEST, e.toString(), e); - return BooleanValue.FALSE; - } - } catch (IllegalStateException e) { - // #2184, some drivers return this on closed connection - saveErrors(new SQLExceptionWrapper(e)); - return BooleanValue.FALSE; - } + // php/431h + if (keepStatementOpen()) { + keepResourceValues(stmt); + } else { + log.log(Level.FINEST, e.toString(), e); + return BooleanValue.FALSE; + } + } catch (IllegalStateException e) { + // #2184, some drivers return this on closed connection + saveErrors(new SQLExceptionWrapper(e)); - if (_rs == null) { - return BooleanValue.TRUE; - } + return BooleanValue.FALSE; + } - return env.wrapJava(_rs); - } + if (_rs == null) { + return BooleanValue.TRUE; + } - private boolean checkSql(ConnectionEntry connEntry, String sql) { - SqlParseToken tok = parseSqlToken(sql, null); + return env.wrapJava(_rs); + } - if (tok == null) { - return false; - } + private boolean checkSql(ConnectionEntry connEntry, String sql) { + SqlParseToken tok = parseSqlToken(sql, null); - switch (tok.getFirstChar()) { - case 'a': - case 'A': { - // drop/alter clears metadata cache - _tableMetadataMap.clear(); - break; - } - case 'd': - case 'D': { - if (tok.matchesToken("DROP")) { - // drop/alter clears metadata cache - _tableMetadataMap.clear(); - - // If DROP is dropping the current database, then clear - // the cached database name in the driver. - // - // php/144a - - tok = parseSqlToken(sql, tok); - - if ((tok != null) && tok.matchesToken("DATABASE")) { - tok = parseSqlToken(sql, tok); - - if (tok != null) { - String dbname = tok.toUnquotedString(); - - if (dbname.equals(_catalog)) { - try { - setCatalog(null); - } catch (SQLException e) { - log.log(Level.FINEST, e.toString(), e); + if (tok == null) { + return false; + } + + switch (tok.getFirstChar()) { + case 'a': + case 'A': { + // drop/alter clears metadata cache + _tableMetadataMap.clear(); + break; + } + case 'd': + case 'D': { + if (tok.matchesToken("DROP")) { + // drop/alter clears metadata cache + _tableMetadataMap.clear(); + + // If DROP is dropping the current database, then clear + // the cached database name in the driver. + // + // php/144a + + tok = parseSqlToken(sql, tok); + + if ((tok != null) && tok.matchesToken("DATABASE")) { + tok = parseSqlToken(sql, tok); + + if (tok != null) { + String dbname = tok.toUnquotedString(); + + if (dbname.equals(_catalog)) { + try { + setCatalog(null); + } catch (SQLException e) { + log.log(Level.FINEST, e.toString(), e); + } + } } - } - } - } + } + } + break; + } + case 'c': + case 'C': { + if (tok.matchesToken("CREATE")) { + // don't pool connections that create tables, because of mysql + // temporary tables + connEntry.markForPoolRemoval(); + } + /* + else if (tok.matchesToken("COMMIT")) { + commit(); + setAutoCommit(true); + return true; + } + */ + break; + } + + // reason for comment out? no real perf gain? + /* + case 'b': case 'B': { + if (tok.matchesToken("BEGIN")) { + // Test for mediawiki performance + setAutoCommit(false); + return true; } break; - } - case 'c': - case 'C': { - if (tok.matchesToken("CREATE")) { - // don't pool connections that create tables, because of mysql - // temporary tables - connEntry.markForPoolRemoval(); } - /* - else if (tok.matchesToken("COMMIT")) { - commit(); + + case 'r': case 'R': { + if (tok.matchesToken("ROLLBACK")) { + rollback(); setAutoCommit(true); return true; } - */ break; - } + } + */ + } + + return false; + } + + /** + * Parse a token from a string containing a SQL statement. + * If the prevToken is null, then the first token in parsed. + * If a SQL token can't be found in the string, then null + * is returned. If a SQL token is found, data is captured in + * the returned SqlParseToken result. + */ + protected SqlParseToken parseSqlToken(String sql, SqlParseToken prevToken) { + if (sql == null) { + _sqlParseToken.init(); + return null; + } - // reason for comment out? no real perf gain? - /* - case 'b': case 'B': { - if (tok.matchesToken("BEGIN")) { - // Test for mediawiki performance - setAutoCommit(false); - return true; - } - break; - } - - case 'r': case 'R': { - if (tok.matchesToken("ROLLBACK")) { - rollback(); - setAutoCommit(true); - return true; - } - break; - } - */ - } - - return false; - } - - /** - * Parse a token from a string containing a SQL statement. - * If the prevToken is null, then the first token in parsed. - * If a SQL token can't be found in the string, then null - * is returned. If a SQL token is found, data is captured in - * the returned SqlParseToken result. - */ - protected SqlParseToken parseSqlToken(String sql, SqlParseToken prevToken) { - if (sql == null) { - _sqlParseToken.init(); - return null; - } - - final int len = sql.length(); - int i, start; - - // Start at index 0, or where we left off last time - - if (prevToken == null) { - i = 0; - } else { - i = prevToken._end; - } - - while (i < len && Character.isWhitespace(sql.charAt(i))) { - i++; - } - - // Must be at least 1 non-whitespace character - - if ((i + 1) >= len) { - _sqlParseToken.init(); - return null; - } - - start = i; - - while (i < len && !Character.isWhitespace(sql.charAt(i))) { - i++; - } - - _sqlParseToken.assign(sql, start, i); - - return _sqlParseToken; - } - - /** - * Creates a database-specific result. - */ - protected JdbcResultResource createResult(Env env, - Statement stmt, - ResultSet rs) { - return new JdbcResultResource(env, stmt, rs, this); - } - - /** - * sets auto-commmit to true or false - */ - public boolean setAutoCommit(boolean mode) { - clearErrors(); - - try { - _conn.getConnection().setAutoCommit(mode); - } catch (SQLException e) { - saveErrors(e); - log.log(Level.FINEST, e.toString(), e); - return false; - } - - return true; - } - - /** - * commits the transaction of the current connection - */ - public boolean commit() { - clearErrors(); - - try { - _conn.getConnection().commit(); - } catch (SQLException e) { - saveErrors(e); - log.log(Level.FINEST, e.toString(), e); - return false; - } - - return true; - } - - /** - * rolls the current transaction back - * - * NOTE: quercus doesn't seem to support the idea - * of savepoints - */ - public boolean rollback() { - clearErrors(); - - try { - _conn.getConnection().rollback(); - } catch (SQLException e) { - saveErrors(e); - log.log(Level.FINEST, e.toString(), e); - return false; - } - - return true; - } - - /** - * Sets the catalog - */ - public void setCatalog(String name) - throws SQLException { - if (_catalog != null && _catalog.equals(name)) { - return; - } - - clearErrors(); - - // php/142v - // mysql jdbc: can't reuse old statements after a USE query - _savedStmt = null; - _freeStmt = null; - - if (!_isUsed && _isCatalogOptimEnabled) { - // The database is only connected, but not used, reopen with - // a real catalog - - ConnectionEntry conn = _conn; - _conn = null; - - if (conn != null) { - conn.phpClose(); - } - - connectInternal(_env, _host, _userName, _password, name, - _port, _socket, _flags, _driver, _url, false); - } else { - _conn.setCatalog(name); - } - - _catalog = name; - } - - /** - * Converts to an object. - */ - public Object toObject() { - return null; - } - - /** - * Converts to a string. - */ - @Override - public String toString() { - if (_conn != null) { - return getClass().getSimpleName() + "[" + _conn.getConnection() + "]"; - } else { - return getClass().getSimpleName() + "[" + null + "]"; - } - } - - /** - * This function is overriden in Postgres to keep - * result set references for php/430a (see also php/1f33) - */ - protected void keepResourceValues(Statement stmt) { - return; - } - - /** - * This function is overriden in Postgres to keep - * statement references for php/430a - */ - protected boolean keepStatementOpen() { - return false; - } - - /** - * Get the current result resource - */ - protected JdbcResultResource getResultResource() { - return _rs; - } - - /** - * Set the current result resource - */ - protected void setResultResource(JdbcResultResource rs) { - _rs = rs; - } - - /** - * This function was added for PostgreSQL pg_last_notice - * - * @return warning messages - */ - protected SQLWarning getWarnings() { - return _warnings; - } - - /** - * Pings the database - */ - public boolean ping(Env env) { - try { - - return isConnected() && !getConnection(env).isClosed(); - - } catch (SQLException e) { - log.log(Level.FINE, e.toString(), e); - env.warning(e.toString(), e); - - return false; - } - } - - /** - * Set the current SQL warnings. - * - * @param warnings the new SQL warnings - */ - protected void setWarnings(SQLWarning warnings) { - _warnings = warnings; - } - - protected void clearErrors() { - _errorMessage = null; - _errorCode = 0; - _warnings = null; - } - - protected void saveErrors(SQLException e) { - _errorMessage = e.getMessage(); - if (_errorMessage == null || "".equals(_errorMessage)) { - _errorMessage = e.toString(); - } - - _errorCode = e.getErrorCode(); - } - - /** - * Returns true if this connection supports TYPE_SCROLL_INSENSITIVE. - * http://bugs.caucho.com/view.php?id=3746 - */ - protected boolean isSeekable() { - return true; - } - - static class TableKey { - - private final String _url; - private final String _catalog; - private final String _schema; - private final String _table; - - TableKey(String url, String catalog, String schema, String table) { - _url = url; - _catalog = catalog; - _schema = schema; - _table = table; - } - - @Override - public int hashCode() { - int hash = 37; - - if (_url != null) { - hash = 65537 * hash + _url.hashCode(); - } - - if (_catalog != null) { - hash = 65537 * hash + _catalog.hashCode(); - } - - if (_schema != null) { - hash = 65537 * hash + _schema.hashCode(); - } - - if (_table != null) { - hash = 65537 * hash + _table.hashCode(); - } - - return hash; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } else if (!(o instanceof TableKey)) { - return false; - } + final int len = sql.length(); + int i, start; - TableKey key = (TableKey) o; + // Start at index 0, or where we left off last time - if ((_url == null) != (key._url == null)) { - return false; - } else if (_url != null && !_url.equals(key._url)) { - return false; - } + if (prevToken == null) { + i = 0; + } else { + i = prevToken._end; + } - if ((_catalog == null) != (key._catalog == null)) { - return false; - } else if (_catalog != null && !_catalog.equals(key._catalog)) { + while (i < len && Character.isWhitespace(sql.charAt(i))) { + i++; + } + + // Must be at least 1 non-whitespace character + + if ((i + 1) >= len) { + _sqlParseToken.init(); + return null; + } + + start = i; + + while (i < len && !Character.isWhitespace(sql.charAt(i))) { + i++; + } + + _sqlParseToken.assign(sql, start, i); + + return _sqlParseToken; + } + + /** + * Creates a database-specific result. + */ + protected JdbcResultResource createResult(Env env, + Statement stmt, + ResultSet rs) { + return new JdbcResultResource(env, stmt, rs, this); + } + + /** + * sets auto-commmit to true or false + */ + public boolean setAutoCommit(boolean mode) { + clearErrors(); + + try { + _conn.getConnection().setAutoCommit(mode); + } catch (SQLException e) { + saveErrors(e); + log.log(Level.FINEST, e.toString(), e); return false; - } + } + + return true; + } - if ((_schema == null) != (key._schema == null)) { + /** + * commits the transaction of the current connection + */ + public boolean commit() { + clearErrors(); + + try { + _conn.getConnection().commit(); + } catch (SQLException e) { + saveErrors(e); + log.log(Level.FINEST, e.toString(), e); return false; - } else if (_schema != null && !_schema.equals(key._schema)) { + } + + return true; + } + + /** + * rolls the current transaction back + * + * NOTE: quercus doesn't seem to support the idea + * of savepoints + */ + public boolean rollback() { + clearErrors(); + + try { + _conn.getConnection().rollback(); + } catch (SQLException e) { + saveErrors(e); + log.log(Level.FINEST, e.toString(), e); return false; - } + } + + return true; + } + + /** + * Sets the catalog + */ + public void setCatalog(String name) + throws SQLException { + if (_catalog != null && _catalog.equals(name)) { + return; + } + + clearErrors(); + + // php/142v + // mysql jdbc: can't reuse old statements after a USE query + _savedStmt = null; + _freeStmt = null; + + if (!_isUsed && _isCatalogOptimEnabled) { + // The database is only connected, but not used, reopen with + // a real catalog + + ConnectionEntry conn = _conn; + _conn = null; + + if (conn != null) { + conn.phpClose(); + } + + connectInternal(_env, _host, _userName, _password, name, + _port, _socket, _flags, _driver, _url, false); + } else { + _conn.setCatalog(name); + } + + _catalog = name; + } + + /** + * Converts to an object. + */ + public Object toObject() { + return null; + } + + /** + * Converts to a string. + */ + @Override + public String toString() { + if (_conn != null) { + return getClass().getSimpleName() + "[" + _conn.getConnection() + "]"; + } else { + return getClass().getSimpleName() + "[" + null + "]"; + } + } + + /** + * This function is overriden in Postgres to keep + * result set references for php/430a (see also php/1f33) + */ + protected void keepResourceValues(Statement stmt) { + return; + } + + /** + * This function is overriden in Postgres to keep + * statement references for php/430a + */ + protected boolean keepStatementOpen() { + return false; + } + + /** + * Get the current result resource + */ + protected JdbcResultResource getResultResource() { + return _rs; + } + + /** + * Set the current result resource + */ + protected void setResultResource(JdbcResultResource rs) { + _rs = rs; + } + + /** + * This function was added for PostgreSQL pg_last_notice + * + * @return warning messages + */ + protected SQLWarning getWarnings() { + return _warnings; + } + + /** + * Pings the database + */ + public boolean ping(Env env) { + try { + + return isConnected() && !getConnection(env).isClosed(); + + } catch (SQLException e) { + log.log(Level.FINE, e.toString(), e); + env.warning(e.toString(), e); - if ((_table == null) != (key._table == null)) { - return false; - } else if (_table != null && !_table.equals(key._table)) { return false; - } - - return true; - } - } - - /* - * This class enables efficient parsing of a SQL token from - * a String. An SQL statement can be parsed one token at a - * time. One can efficiently check that first letter of - * the parse token via matchesFirstChar() without creating - * a substring from the original. - */ - protected static class SqlParseToken { - - private String _query; - private String _token; - private int _start; - private int _end; - private char _firstChar; - - public void init() { - _query = null; - _token = null; - _start = -1; - _end = -1; - _firstChar = '\0'; - } - - public void assign(String query, int start, int end) { - _query = query; - _token = null; - _start = start; - _end = end; - _firstChar = query.charAt(start); - } - - public boolean matchesFirstChar(char upper, char lower) { - return (_firstChar == upper) || (_firstChar == lower); - } - - public char getFirstChar() { - return _firstChar; - } - - // Case insensitive compare of token string - public boolean matchesToken(String token) { - if (_token == null) { - _token = _query.substring(_start, _end); - } - - return _token.equalsIgnoreCase(token); - } - - @Override - public String toString() { - if (_token == null) { - _token = _query.substring(_start, _end); - } - - return _token; - } - - /* - * Return the SQL token as a string. If the token - * is back quoted, then remove the back quotes and - * return the string inside. - */ - public String toUnquotedString() { - String tok = toString(); - - // Extract database name if back quoted : "DROP DATABASE `DBNAME`" - - if (tok.length() >= 2 - && tok.charAt(0) == '`' - && tok.charAt(tok.length() - 1) == '`') { - tok = tok.substring(1, tok.length() - 1); - } - - return tok; - } - } + } + } + + /** + * Set the current SQL warnings. + * + * @param warnings the new SQL warnings + */ + protected void setWarnings(SQLWarning warnings) { + _warnings = warnings; + } + + protected void clearErrors() { + _errorMessage = null; + _errorCode = 0; + _warnings = null; + } + + protected void saveErrors(SQLException e) { + _errorMessage = e.getMessage(); + if (_errorMessage == null || "".equals(_errorMessage)) { + _errorMessage = e.toString(); + } + + _errorCode = e.getErrorCode(); + } + + /** + * Returns true if this connection supports TYPE_SCROLL_INSENSITIVE. + * http://bugs.caucho.com/view.php?id=3746 + */ + protected boolean isSeekable() { + return true; + } + + static class TableKey { + + private final String _url; + private final String _catalog; + private final String _schema; + private final String _table; + + TableKey(String url, String catalog, String schema, String table) { + _url = url; + _catalog = catalog; + _schema = schema; + _table = table; + } + + @Override + public int hashCode() { + int hash = 37; + + if (_url != null) { + hash = 65537 * hash + _url.hashCode(); + } + + if (_catalog != null) { + hash = 65537 * hash + _catalog.hashCode(); + } + + if (_schema != null) { + hash = 65537 * hash + _schema.hashCode(); + } + + if (_table != null) { + hash = 65537 * hash + _table.hashCode(); + } + + return hash; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (!(o instanceof TableKey)) { + return false; + } + + TableKey key = (TableKey) o; + + if ((_url == null) != (key._url == null)) { + return false; + } else if (_url != null && !_url.equals(key._url)) { + return false; + } + + if ((_catalog == null) != (key._catalog == null)) { + return false; + } else if (_catalog != null && !_catalog.equals(key._catalog)) { + return false; + } + + if ((_schema == null) != (key._schema == null)) { + return false; + } else if (_schema != null && !_schema.equals(key._schema)) { + return false; + } + + if ((_table == null) != (key._table == null)) { + return false; + } else if (_table != null && !_table.equals(key._table)) { + return false; + } + + return true; + } + } + + /* + * This class enables efficient parsing of a SQL token from + * a String. An SQL statement can be parsed one token at a + * time. One can efficiently check that first letter of + * the parse token via matchesFirstChar() without creating + * a substring from the original. + */ + protected static class SqlParseToken { + + private String _query; + private String _token; + private int _start; + private int _end; + private char _firstChar; + + public void init() { + _query = null; + _token = null; + _start = -1; + _end = -1; + _firstChar = '\0'; + } + + public void assign(String query, int start, int end) { + _query = query; + _token = null; + _start = start; + _end = end; + _firstChar = query.charAt(start); + } + + public boolean matchesFirstChar(char upper, char lower) { + return (_firstChar == upper) || (_firstChar == lower); + } + + public char getFirstChar() { + return _firstChar; + } + + // Case insensitive compare of token string + public boolean matchesToken(String token) { + if (_token == null) { + _token = _query.substring(_start, _end); + } + + return _token.equalsIgnoreCase(token); + } + + @Override + public String toString() { + if (_token == null) { + _token = _query.substring(_start, _end); + } + + return _token; + } + + /* + * Return the SQL token as a string. If the token + * is back quoted, then remove the back quotes and + * return the string inside. + */ + public String toUnquotedString() { + String tok = toString(); + + // Extract database name if back quoted : "DROP DATABASE `DBNAME`" + + if (tok.length() >= 2 + && tok.charAt(0) == '`' + && tok.charAt(tok.length() - 1) == '`') { + tok = tok.substring(1, tok.length() - 1); + } + + return tok; + } + } } diff --git a/quercus/src/main/java/com/caucho/quercus/lib/db/Postgres.java b/quercus/src/main/java/com/caucho/quercus/lib/db/Postgres.java index a417a08..3197e2d 100644 --- a/quercus/src/main/java/com/caucho/quercus/lib/db/Postgres.java +++ b/quercus/src/main/java/com/caucho/quercus/lib/db/Postgres.java @@ -50,324 +50,329 @@ @ResourceType("pgsql link") public class Postgres extends JdbcConnectionResource { - private static final Logger log = Logger.getLogger(Postgres.class.getName()); - private static final L10N L = new L10N(Postgres.class); - PostgresResult _asyncResult; - PostgresStatement _asyncStmt; - // named prepared statements for postgres - private HashMap _stmtTable = new HashMap(); - // Postgres specific server error message - // org.postgresql.util.ServerErrorMessage - Object _serverErrorMessage; - - public Postgres(Env env, - @Optional("localhost") String host, - @Optional String user, - @Optional String password, - @Optional String db, - @Optional("5432") int port, - @Optional String driver, - @Optional String url) { - super(env); - - connectInternal(env, host, user, password, db, port, "", 0, - driver, url, false); - } - - /** - * Connects to the underlying database. - */ - @Override - protected ConnectionEntry connectImpl(Env env, - String host, - String userName, - String password, - String dbname, - int port, - String socket, - int flags, - String driver, - String url, - boolean isNewLink) { - if (isConnected()) { - env.warning(L.l("Connection is already opened to '{0}'", this)); - return null; - } - - try { - - if (host == null || host.equals("")) { - host = "localhost"; - } - - if (driver == null || driver.equals("")) { - driver = "org.postgresql.Driver"; - } - - if (url == null || url.equals("")) { - url = "jdbc:postgresql://" + host + ":" + port + "/" + dbname; - } - - ConnectionEntry jConn; - - jConn = env.getConnection(driver, url, userName, password, !isNewLink); - - return jConn; - } catch (SQLException e) { - env.warning( - "A link to the server could not be established. " + e.toString()); - env.setSpecialValue( - "postgres.connectErrno", LongValue.create(e.getErrorCode())); - env.setSpecialValue( - "postgres.connectError", env.createString(e.getMessage())); - - log.log(Level.FINE, e.toString(), e); - - return null; - } catch (Exception e) { - env.warning( - "A link to the server could not be established. " + e.toString()); - env.setSpecialValue( - "postgres.connectError", env.createString(e.getMessage())); - - log.log(Level.FINE, e.toString(), e); - return null; - } - } - - /** - * returns a prepared statement - */ - public PostgresStatement prepare(Env env, StringValue query) { - PostgresStatement stmt = new PostgresStatement( - (Postgres) validateConnection()); - - stmt.prepare(env, query); - - return stmt; - } - - /** - * Executes a query. - * - * @param sql the escaped query string - * (can contain escape sequences like `\n' and `\Z') - * - * @return a {@link JdbcResultResource}, or null for failure - */ - public PostgresResult query(Env env, String sql) { - SqlParseToken tok = parseSqlToken(sql, null); - - if (tok != null - && tok.matchesFirstChar('S', 's') - && tok.matchesToken("SET")) { - // Check for "SET CLIENT_ENCODING TO ..." - - tok = parseSqlToken(sql, tok); - - if (tok != null && tok.matchesToken("CLIENT_ENCODING")) { + private static final Logger log = Logger.getLogger(Postgres.class.getName()); + private static final L10N L = new L10N(Postgres.class); + PostgresResult _asyncResult; + PostgresStatement _asyncStmt; + // named prepared statements for postgres + private HashMap _stmtTable = new HashMap(); + // Postgres specific server error message + // org.postgresql.util.ServerErrorMessage + Object _serverErrorMessage; + + public Postgres(Env env, + @Optional("localhost") String host, + @Optional String user, + @Optional String password, + @Optional String db, + @Optional("5432") int port, + @Optional String driver, + @Optional String url) { + super(env); + + connectInternal(env, host, user, password, db, port, "", 0, + driver, url, false); + } + + public Postgres(Env env, String jndiPath) { + super(env); + connectInternalJNDI(env, jndiPath); + } + + /** + * Connects to the underlying database. + */ + @Override + protected ConnectionEntry connectImpl(Env env, + String host, + String userName, + String password, + String dbname, + int port, + String socket, + int flags, + String driver, + String url, + boolean isNewLink) { + if (isConnected()) { + env.warning(L.l("Connection is already opened to '{0}'", this)); + return null; + } + + try { + + if (host == null || host.equals("")) { + host = "localhost"; + } + + if (driver == null || driver.equals("")) { + driver = "org.postgresql.Driver"; + } + + if (url == null || url.equals("")) { + url = "jdbc:postgresql://" + host + ":" + port + "/" + dbname; + } + + ConnectionEntry jConn; + + jConn = env.getConnection(driver, url, userName, password, !isNewLink); + + return jConn; + } catch (SQLException e) { + env.warning( + "A link to the server could not be established. " + e.toString()); + env.setSpecialValue( + "postgres.connectErrno", LongValue.create(e.getErrorCode())); + env.setSpecialValue( + "postgres.connectError", env.createString(e.getMessage())); + + log.log(Level.FINE, e.toString(), e); + + return null; + } catch (Exception e) { + env.warning( + "A link to the server could not be established. " + e.toString()); + env.setSpecialValue( + "postgres.connectError", env.createString(e.getMessage())); + + log.log(Level.FINE, e.toString(), e); + return null; + } + } + + /** + * returns a prepared statement + */ + public PostgresStatement prepare(Env env, StringValue query) { + PostgresStatement stmt = new PostgresStatement( + (Postgres) validateConnection()); + + stmt.prepare(env, query); + + return stmt; + } + + /** + * Executes a query. + * + * @param sql the escaped query string + * (can contain escape sequences like `\n' and `\Z') + * + * @return a {@link JdbcResultResource}, or null for failure + */ + public PostgresResult query(Env env, String sql) { + SqlParseToken tok = parseSqlToken(sql, null); + + if (tok != null + && tok.matchesFirstChar('S', 's') + && tok.matchesToken("SET")) { + // Check for "SET CLIENT_ENCODING TO ..." + tok = parseSqlToken(sql, tok); - if (tok != null && tok.matchesToken("TO")) { - // Ignore any attempt to change the CLIENT_ENCODING since - // the JDBC driver for Postgres only supports UNICODE. - // Execute no-op SQL statement since we need to return - // a valid SQL result to the caller. + if (tok != null && tok.matchesToken("CLIENT_ENCODING")) { + tok = parseSqlToken(sql, tok); - sql = "SET CLIENT_ENCODING TO 'UNICODE'"; + if (tok != null && tok.matchesToken("TO")) { + // Ignore any attempt to change the CLIENT_ENCODING since + // the JDBC driver for Postgres only supports UNICODE. + // Execute no-op SQL statement since we need to return + // a valid SQL result to the caller. + + sql = "SET CLIENT_ENCODING TO 'UNICODE'"; + } + } + } + + Object result = realQuery(env, sql).toJavaObject(); + + if (!(result instanceof PostgresResult)) { + return null; + } + + return (PostgresResult) result; + } + + /** + * Creates a database-specific result. + */ + @Override + protected JdbcResultResource createResult(Env env, + Statement stmt, + ResultSet rs) { + return new PostgresResult(env, stmt, rs, this); + } + + public void setAsynchronousResult(PostgresResult asyncResult) { + _asyncResult = asyncResult; + } + + public PostgresResult getAsynchronousResult() { + return _asyncResult; + } + + public PostgresStatement getAsynchronousStatement() { + return _asyncStmt; + } + + public void setAsynchronousStatement(PostgresStatement asyncStmt) { + _asyncStmt = asyncStmt; + } + + public void putStatement(String name, + PostgresStatement stmt) { + _stmtTable.put(name, stmt); + } + + public PostgresStatement getStatement(String name) { + return _stmtTable.get(name); + } + + public PostgresStatement removeStatement(String name) { + return _stmtTable.remove(name); + } + + /** + * This function is overriden in Postgres to keep + * result set references for php/430a (see also php/1f33) + */ + @Override + protected void keepResourceValues(Statement stmt) { + setResultResource(createResult(getEnv(), stmt, null)); + } + + /** + * This function is overriden in Postgres to keep + * statement references for php/430a + */ + @Override + protected boolean keepStatementOpen() { + return true; + } + + static public StringValue pgRealEscapeString(StringValue str) { + StringValue buf = str.createStringBuilder(str.length()); + + final int strLength = str.length(); + + for (int i = 0; i < strLength; i++) { + char c = str.charAt(i); + + switch (c) { + case '\u0000': + buf.append('\\'); + buf.append('\u0000'); + break; + case '\n': + buf.append('\\'); + buf.append('n'); + break; + case '\r': + buf.append('\\'); + buf.append('r'); + break; + case '\\': + buf.append('\\'); + buf.append('\\'); + break; + case '\'': + buf.append('\''); + buf.append('\''); + break; + case '"': + // pg_escape_string does nothing about it. + // buf.append('\\'); + buf.append('\"'); + break; + case '\032': + buf.append('\\'); + buf.append('Z'); + break; + default: + buf.append(c); + break; } - } - } - - Object result = realQuery(env, sql).toJavaObject(); - - if (!(result instanceof PostgresResult)) { - return null; - } - - return (PostgresResult) result; - } - - /** - * Creates a database-specific result. - */ - @Override - protected JdbcResultResource createResult(Env env, - Statement stmt, - ResultSet rs) { - return new PostgresResult(env, stmt, rs, this); - } - - public void setAsynchronousResult(PostgresResult asyncResult) { - _asyncResult = asyncResult; - } - - public PostgresResult getAsynchronousResult() { - return _asyncResult; - } - - public PostgresStatement getAsynchronousStatement() { - return _asyncStmt; - } - - public void setAsynchronousStatement(PostgresStatement asyncStmt) { - _asyncStmt = asyncStmt; - } - - public void putStatement(String name, - PostgresStatement stmt) { - _stmtTable.put(name, stmt); - } - - public PostgresStatement getStatement(String name) { - return _stmtTable.get(name); - } - - public PostgresStatement removeStatement(String name) { - return _stmtTable.remove(name); - } - - /** - * This function is overriden in Postgres to keep - * result set references for php/430a (see also php/1f33) - */ - @Override - protected void keepResourceValues(Statement stmt) { - setResultResource(createResult(getEnv(), stmt, null)); - } - - /** - * This function is overriden in Postgres to keep - * statement references for php/430a - */ - @Override - protected boolean keepStatementOpen() { - return true; - } - - static public StringValue pgRealEscapeString(StringValue str) { - StringValue buf = str.createStringBuilder(str.length()); - - final int strLength = str.length(); - - for (int i = 0; i < strLength; i++) { - char c = str.charAt(i); - - switch (c) { - case '\u0000': - buf.append('\\'); - buf.append('\u0000'); - break; - case '\n': - buf.append('\\'); - buf.append('n'); - break; - case '\r': - buf.append('\\'); - buf.append('r'); - break; - case '\\': - buf.append('\\'); - buf.append('\\'); - break; - case '\'': - buf.append('\''); - buf.append('\''); - break; - case '"': - // pg_escape_string does nothing about it. - // buf.append('\\'); - buf.append('\"'); - break; - case '\032': - buf.append('\\'); - buf.append('Z'); - break; - default: - buf.append(c); - break; - } - } - - return buf; - } - - /** - * Escape the given string for SQL statements. - * - * @param str a string - * @return the string escaped for SQL statements - */ - @Override - protected StringValue realEscapeString(StringValue str) { - return pgRealEscapeString(str); - } - - /** - * This function is overriden in Postgres to clear - * any postgres specific server error message - */ - @Override - protected void clearErrors() { - super.clearErrors(); - _serverErrorMessage = null; - } - - /** - * This function is overriden in Postgres to save - * the postgres specific server error message - */ - @Override - protected void saveErrors(SQLException e) { - try { - super.saveErrors(e); - - // Get the postgres specific server error message - Class cl = Class.forName("org.postgresql.util.PSQLException"); - Method method = cl.getDeclaredMethod("getServerErrorMessage", (Class) null); - _serverErrorMessage = method.invoke(e, new Object[]{}); - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - } - } - - /** - * Return the postgres server specific error message - */ - protected Object getServerErrorMessage() { - return _serverErrorMessage; - } - - @Override - public String toString() { - if (isConnected()) { - return "Postgres[" + getHost() + "]"; - } else { - return "Postgres[]"; - } - } - - /** - * Return the "client_encoding" property. This is the - * encoding the JDBC driver uses to read character - * data from the server. The JDBC driver used to let - * the user change the encoding, but it now fails on - * any attempt to set the encoding to anything other - * than UNICODE. - */ - @Override - public String getClientEncoding() { - return "UNICODE"; - } - - /** - * Set the "client_encoding" property. This is - * a no-op for the JDBC driver because it only - * supports UNICODE as the client encoding. - * Return true to indicate success in all cases. - */ - @Override - public boolean setClientEncoding(String encoding) { - return true; - } + } + + return buf; + } + + /** + * Escape the given string for SQL statements. + * + * @param str a string + * @return the string escaped for SQL statements + */ + @Override + protected StringValue realEscapeString(StringValue str) { + return pgRealEscapeString(str); + } + + /** + * This function is overriden in Postgres to clear + * any postgres specific server error message + */ + @Override + protected void clearErrors() { + super.clearErrors(); + _serverErrorMessage = null; + } + + /** + * This function is overriden in Postgres to save + * the postgres specific server error message + */ + @Override + protected void saveErrors(SQLException e) { + try { + super.saveErrors(e); + + // Get the postgres specific server error message + Class cl = Class.forName("org.postgresql.util.PSQLException"); + Method method = cl.getDeclaredMethod("getServerErrorMessage", (Class) null); + _serverErrorMessage = method.invoke(e, new Object[]{}); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + } + } + + /** + * Return the postgres server specific error message + */ + protected Object getServerErrorMessage() { + return _serverErrorMessage; + } + + @Override + public String toString() { + if (isConnected()) { + return "Postgres[" + getHost() + "]"; + } else { + return "Postgres[]"; + } + } + + /** + * Return the "client_encoding" property. This is the + * encoding the JDBC driver uses to read character + * data from the server. The JDBC driver used to let + * the user change the encoding, but it now fails on + * any attempt to set the encoding to anything other + * than UNICODE. + */ + @Override + public String getClientEncoding() { + return "UNICODE"; + } + + /** + * Set the "client_encoding" property. This is + * a no-op for the JDBC driver because it only + * supports UNICODE as the client encoding. + * Return true to indicate success in all cases. + */ + @Override + public boolean setClientEncoding(String encoding) { + return true; + } } diff --git a/quercus/src/main/java/com/caucho/quercus/lib/db/PostgresModule.java b/quercus/src/main/java/com/caucho/quercus/lib/db/PostgresModule.java index dc725e5..786ace6 100644 --- a/quercus/src/main/java/com/caucho/quercus/lib/db/PostgresModule.java +++ b/quercus/src/main/java/com/caucho/quercus/lib/db/PostgresModule.java @@ -64,3421 +64,3430 @@ */ public class PostgresModule extends AbstractQuercusModule { - private static final Logger log = Log.open(PostgresModule.class); - private static final L10N L = new L10N(PostgresModule.class); - public static final int PGSQL_ASSOC = 0x01; - public static final int PGSQL_NUM = 0x02; - public static final int PGSQL_BOTH = 0x03; - public static final int PGSQL_CONNECT_FORCE_NEW = 0x04; - public static final int PGSQL_CONNECTION_BAD = 0x05; - public static final int PGSQL_CONNECTION_OK = 0x06; - public static final int PGSQL_SEEK_SET = 0x07; - public static final int PGSQL_SEEK_CUR = 0x08; - public static final int PGSQL_SEEK_END = 0x09; - public static final int PGSQL_EMPTY_QUERY = 0x0A; - public static final int PGSQL_COMMAND_OK = 0x0B; - public static final int PGSQL_TUPLES_OK = 0x0C; - public static final int PGSQL_COPY_OUT = 0x0D; - public static final int PGSQL_COPY_IN = 0x0E; - public static final int PGSQL_BAD_RESPONSE = 0x0F; - public static final int PGSQL_NONFATAL_ERROR = 0x10; - public static final int PGSQL_FATAL_ERROR = 0x11; - public static final int PGSQL_TRANSACTION_IDLE = 0x12; - public static final int PGSQL_TRANSACTION_ACTIVE = 0x13; - public static final int PGSQL_TRANSACTION_INTRANS = 0x14; - public static final int PGSQL_TRANSACTION_INERROR = 0x15; - public static final int PGSQL_TRANSACTION_UNKNOWN = 0x16; - public static final int PGSQL_DIAG_SEVERITY = 0x17; - public static final int PGSQL_DIAG_SQLSTATE = 0x18; - public static final int PGSQL_DIAG_MESSAGE_PRIMARY = 0x19; - public static final int PGSQL_DIAG_MESSAGE_DETAIL = 0x20; - public static final int PGSQL_DIAG_MESSAGE_HINT = 0x21; - public static final int PGSQL_DIAG_STATEMENT_POSITION = 0x22; - public static final int PGSQL_DIAG_INTERNAL_POSITION = 0x23; - public static final int PGSQL_DIAG_INTERNAL_QUERY = 0x24; - public static final int PGSQL_DIAG_CONTEXT = 0x25; - public static final int PGSQL_DIAG_SOURCE_FILE = 0x26; - public static final int PGSQL_DIAG_SOURCE_LINE = 0x27; - public static final int PGSQL_DIAG_SOURCE_FUNCTION = 0x28; - public static final int PGSQL_ERRORS_TERSE = 0x29; - public static final int PGSQL_ERRORS_DEFAULT = 0x2A; - public static final int PGSQL_ERRORS_VERBOSE = 0x2B; - public static final int PGSQL_STATUS_LONG = 0x2C; - public static final int PGSQL_STATUS_STRING = 0x2D; - public static final int PGSQL_CONV_IGNORE_DEFAULT = 0x2E; - public static final int PGSQL_CONV_FORCE_NULL = 0x2F; - - /** - * Constructor - */ - public PostgresModule() { - } - - /** - * Returns true for the postgres extension. - */ - @Override - public String[] getLoadedExtensions() { - return new String[]{"postgres", "pgsql"}; - } - - /** - * Returns number of affected records (tuples) - */ - public static int pg_affected_rows(Env env, - @NotNull PostgresResult result) { - try { - if (result == null) { + private static final Logger log = Log.open(PostgresModule.class); + private static final L10N L = new L10N(PostgresModule.class); + public static final int PGSQL_ASSOC = 0x01; + public static final int PGSQL_NUM = 0x02; + public static final int PGSQL_BOTH = 0x03; + public static final int PGSQL_CONNECT_FORCE_NEW = 0x04; + public static final int PGSQL_CONNECTION_BAD = 0x05; + public static final int PGSQL_CONNECTION_OK = 0x06; + public static final int PGSQL_SEEK_SET = 0x07; + public static final int PGSQL_SEEK_CUR = 0x08; + public static final int PGSQL_SEEK_END = 0x09; + public static final int PGSQL_EMPTY_QUERY = 0x0A; + public static final int PGSQL_COMMAND_OK = 0x0B; + public static final int PGSQL_TUPLES_OK = 0x0C; + public static final int PGSQL_COPY_OUT = 0x0D; + public static final int PGSQL_COPY_IN = 0x0E; + public static final int PGSQL_BAD_RESPONSE = 0x0F; + public static final int PGSQL_NONFATAL_ERROR = 0x10; + public static final int PGSQL_FATAL_ERROR = 0x11; + public static final int PGSQL_TRANSACTION_IDLE = 0x12; + public static final int PGSQL_TRANSACTION_ACTIVE = 0x13; + public static final int PGSQL_TRANSACTION_INTRANS = 0x14; + public static final int PGSQL_TRANSACTION_INERROR = 0x15; + public static final int PGSQL_TRANSACTION_UNKNOWN = 0x16; + public static final int PGSQL_DIAG_SEVERITY = 0x17; + public static final int PGSQL_DIAG_SQLSTATE = 0x18; + public static final int PGSQL_DIAG_MESSAGE_PRIMARY = 0x19; + public static final int PGSQL_DIAG_MESSAGE_DETAIL = 0x20; + public static final int PGSQL_DIAG_MESSAGE_HINT = 0x21; + public static final int PGSQL_DIAG_STATEMENT_POSITION = 0x22; + public static final int PGSQL_DIAG_INTERNAL_POSITION = 0x23; + public static final int PGSQL_DIAG_INTERNAL_QUERY = 0x24; + public static final int PGSQL_DIAG_CONTEXT = 0x25; + public static final int PGSQL_DIAG_SOURCE_FILE = 0x26; + public static final int PGSQL_DIAG_SOURCE_LINE = 0x27; + public static final int PGSQL_DIAG_SOURCE_FUNCTION = 0x28; + public static final int PGSQL_ERRORS_TERSE = 0x29; + public static final int PGSQL_ERRORS_DEFAULT = 0x2A; + public static final int PGSQL_ERRORS_VERBOSE = 0x2B; + public static final int PGSQL_STATUS_LONG = 0x2C; + public static final int PGSQL_STATUS_STRING = 0x2D; + public static final int PGSQL_CONV_IGNORE_DEFAULT = 0x2E; + public static final int PGSQL_CONV_FORCE_NULL = 0x2F; + + /** + * Constructor + */ + public PostgresModule() { + } + + /** + * Returns true for the postgres extension. + */ + @Override + public String[] getLoadedExtensions() { + return new String[]{"postgres", "pgsql"}; + } + + /** + * Returns number of affected records (tuples) + */ + public static int pg_affected_rows(Env env, + @NotNull PostgresResult result) { + try { + if (result == null) { + return -1; + } + + return result.getAffectedRows(); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return 0; + } + } + + /** + * pg_affected_rows() alias. + */ + public static int pg_cmdtuples(Env env, + @NotNull PostgresResult result) { + if (result == null) { return -1; - } - - return result.getAffectedRows(); - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return 0; - } - } - - /** - * pg_affected_rows() alias. - */ - public static int pg_cmdtuples(Env env, - @NotNull PostgresResult result) { - if (result == null) { - return -1; - } - - return pg_affected_rows(env, result); - } - - /** - * Cancel an asynchronous query - */ - public static boolean pg_cancel_query(Env env, - @NotNull Postgres conn) { - try { - if (conn == null) { - return false; - } - - conn.setAsynchronousStatement(null); - conn.setAsynchronousResult(null); - - return true; - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return false; - } - } - - /** - * Gets the client encoding - */ - @ReturnNullAsFalse - public static String pg_client_encoding(Env env, - @Optional Postgres conn) { - try { - if (conn == null) { - conn = getConnection(env); - } - - return conn.getClientEncoding(); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Closes a PostgreSQL connection - */ - public static boolean pg_close(Env env, - @Optional Postgres conn) { - try { - if (conn == null) { + } + + return pg_affected_rows(env, result); + } + + /** + * Cancel an asynchronous query + */ + public static boolean pg_cancel_query(Env env, + @NotNull Postgres conn) { + try { + if (conn == null) { + return false; + } + + conn.setAsynchronousStatement(null); + conn.setAsynchronousResult(null); + + return true; + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return false; - } - - if (conn == env.getSpecialValue("caucho.postgres")) { - env.removeSpecialValue("caucho.postgres"); - } - - conn.close(env); - - return true; - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - } - - return false; - } - - /** - * Open a PostgreSQL connection - */ - @ReturnNullAsFalse - public static Postgres pg_connect(Env env, - String connectionString, - @Optional int connectionType) { - try { - String host = "localhost"; - int port = 5432; - String dbName = ""; - String userName = ""; - String password = ""; - - HashMap nameValueMap = parseConnectionString(connectionString); - - String value = nameValueMap.get("host"); - if (value != null) { - host = nameValueMap.get("host"); - } - - value = nameValueMap.get("port"); - if (value != null) { - port = 0; - int len = value.length(); - - for (int i = 0; i < len; i++) { - char ch = value.charAt(i); - - if ('0' <= ch && ch <= '9') { - port = port * 10 + value.charAt(i) - '0'; - } else { - break; - } - } - } - - value = nameValueMap.get("dbname"); - if (value != null) { - dbName = value; - } - - value = nameValueMap.get("user"); - if (value != null) { - userName = value; - } - - value = nameValueMap.get("password"); - if (value != null) { - password = value; - } - - String driver = "org.postgresql.Driver"; - String url = "jdbc:postgresql://" + host + ":" + port + "/" + dbName; - - Postgres postgres = new Postgres( - env, host, userName, password, dbName, port, driver, url); - - if (!postgres.isConnected()) { + } + } + + /** + * Gets the client encoding + */ + @ReturnNullAsFalse + public static String pg_client_encoding(Env env, + @Optional Postgres conn) { + try { + if (conn == null) { + conn = getConnection(env); + } + + return conn.getClientEncoding(); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } + } + } + + /** + * Closes a PostgreSQL connection + */ + public static boolean pg_close(Env env, + @Optional Postgres conn) { + try { + if (conn == null) { + return false; + } - env.setSpecialValue("caucho.postgres", postgres); + if (conn == env.getSpecialValue("caucho.postgres")) { + env.removeSpecialValue("caucho.postgres"); + } - return postgres; + conn.close(env); - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } + return true; + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + } + + return false; + } + + /** + * Open a PostgreSQL connection + */ + @ReturnNullAsFalse + public static Postgres pg_connect(Env env, + String connectionString, + @Optional int connectionType) { + try { + String host = "localhost"; + int port = 5432; + String dbName = ""; + String userName = ""; + String password = ""; + if (connectionString.substring(0, 5).equals("java:")) { + Postgres postgres = new Postgres(env, connectionString); + if (!postgres.isConnected()) { + return null; + } + env.setSpecialValue("caucho.postgres", postgres); + return postgres; + } - /** - * Returns the name/value pairs from the postgres connection string. - */ - private static HashMap parseConnectionString(String s) { - HashMap map = new HashMap(); + HashMap nameValueMap = parseConnectionString(connectionString); - char ch; - int len = s.length(); + String value = nameValueMap.get("host"); + if (value != null) { + host = nameValueMap.get("host"); + } - int i = 0; + value = nameValueMap.get("port"); + if (value != null) { + port = 0; + int len = value.length(); - CharBuffer buffer = new CharBuffer(); + for (int i = 0; i < len; i++) { + char ch = value.charAt(i); - while (i < len) { - buffer.clear(); + if ('0' <= ch && ch <= '9') { + port = port * 10 + value.charAt(i) - '0'; + } else { + break; + } + } + } - // skip whitespace - for (; i < len && Character.isWhitespace(ch = s.charAt(i)); i++) { - } + value = nameValueMap.get("dbname"); + if (value != null) { + dbName = value; + } - // get name - for (; - i < len && !Character.isWhitespace(ch = s.charAt(i)) - && ch != '='; i++) { - buffer.append(ch); - } + value = nameValueMap.get("user"); + if (value != null) { + userName = value; + } - String name = buffer.toString(); - buffer.clear(); + value = nameValueMap.get("password"); + if (value != null) { + password = value; + } - // skip until '=' - while (i < len && (ch = s.charAt(i++)) != '=') { - } + String driver = "org.postgresql.Driver"; + String url = "jdbc:postgresql://" + host + ":" + port + "/" + dbName; - // skip whitespace - for (; i < len && Character.isWhitespace(ch = s.charAt(i)); i++) { - } + Postgres postgres = new Postgres( + env, host, userName, password, dbName, port, driver, url); - boolean isQuoted = false; + if (!postgres.isConnected()) { + return null; + } - // value may be quoted - if (i < len) { - if ((ch = s.charAt(i++)) == '\'') { - isQuoted = true; - } else { - buffer.append(ch); - } - } - - boolean isEscaped = false; - - // get value - loop: - while (i < len) { - ch = s.charAt(i++); - - switch (ch) { - case '\\': - if (isEscaped) { - buffer.append(ch); - } - - isEscaped = !isEscaped; - break; - - case '\'': - if (isEscaped) { - buffer.append(ch); - isEscaped = false; - break; - } else if (isQuoted) { - break loop; - } - - case ' ': - case '\n': - case '\r': - case '\f': - case '\t': - if (isQuoted) { - buffer.append(ch); - break; - } else if (isEscaped) { - buffer.append('\\'); - break loop; - } else { - break loop; - } - - default: - if (isEscaped) { - buffer.append('\\'); - isEscaped = false; - } - - buffer.append(ch); - } - } - - String value = buffer.toString(); - - if (name.length() > 0) { - map.put(name, value); - } - } - - return map; - } - - /** - * Get connection is busy or not - */ - public static boolean pg_connection_busy(Env env, - @NotNull Postgres conn) { - // Always return false, for now (pg_send_xxxx are not asynchronous) - // so there should be no reason for a connection to become busy in - // between different pg_xxx calls. - - return false; - } - - /** - * Reset connection (reconnect) - */ - public static boolean pg_connection_reset(Env env, - @NotNull Postgres conn) { - try { - if (conn == null) { - return false; - } + env.setSpecialValue("caucho.postgres", postgres); - // Query database name before closing connection + return postgres; - String dbname = conn.getDbName(); + } catch (Exception ex) { + System.err.println(ex.toString()); + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } - conn.close(env); + /** + * Returns the name/value pairs from the postgres connection string. + */ + private static HashMap parseConnectionString(String s) { + HashMap map = new HashMap(); - conn = new Postgres(env, - conn.getHost(), - conn.getUserName(), - conn.getPassword(), - dbname, - conn.getPort(), - conn.getDriver(), - conn.getUrl()); + char ch; + int len = s.length(); - env.setSpecialValue("caucho.postgres", conn); + int i = 0; - return true; + CharBuffer buffer = new CharBuffer(); - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return false; - } - } + while (i < len) { + buffer.clear(); - /** - * Get connection status - */ - public static int pg_connection_status(Env env, - @NotNull Postgres conn) { - try { - if (conn == null) { - return PGSQL_CONNECTION_BAD; - } - - boolean ping = pg_ping(env, conn); - - return ping ? PGSQL_CONNECTION_OK : PGSQL_CONNECTION_BAD; - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return PGSQL_CONNECTION_BAD; - } - } - - /** - * Convert associative array values into suitable for SQL statement - */ - @ReturnNullAsFalse - public static ArrayValue pg_convert(Env env, - @NotNull Postgres conn, - String tableName, - ArrayValue assocArray, - @Optional("0") int options) { - try { - if (conn == null) { - return null; - } + // skip whitespace + for (; i < len && Character.isWhitespace(ch = s.charAt(i)); i++) { + } - // TODO: options has not been implemented yet. + // get name + for (; + i < len && !Character.isWhitespace(ch = s.charAt(i)) + && ch != '='; i++) { + buffer.append(ch); + } - // TODO: the following PHP note has not been implemented yet. - // Note: If there are boolean fields in table_name don't use - // the constant TRUE in assoc_array. It will be converted to the - // string 'TRUE' which is no valid entry for boolean fields in - // PostgreSQL. Use one of t, true, 1, y, yes instead. + String name = buffer.toString(); + buffer.clear(); - if (options > 0) { - throw new UnimplementedException("pg_convert with options"); - } + // skip until '=' + while (i < len && (ch = s.charAt(i++)) != '=') { + } - PostgresResult result; + // skip whitespace + for (; i < len && Character.isWhitespace(ch = s.charAt(i)); i++) { + } - Connection jdbcConn = conn.getJavaConnection(); - DatabaseMetaData dbMetaData = jdbcConn.getMetaData(); + boolean isQuoted = false; - ResultSet rs = dbMetaData.getColumns("", "", tableName, ""); + // value may be quoted + if (i < len) { + if ((ch = s.charAt(i++)) == '\'') { + isQuoted = true; + } else { + buffer.append(ch); + } + } - // Check column count - ResultSetMetaData rsMetaData = rs.getMetaData(); - int n = rsMetaData.getColumnCount(); - if (n < assocArray.getSize()) { - return null; - } + boolean isEscaped = false; + + // get value + loop: + while (i < len) { + ch = s.charAt(i++); + + switch (ch) { + case '\\': + if (isEscaped) { + buffer.append(ch); + } + + isEscaped = !isEscaped; + break; + + case '\'': + if (isEscaped) { + buffer.append(ch); + isEscaped = false; + break; + } else if (isQuoted) { + break loop; + } + + case ' ': + case '\n': + case '\r': + case '\f': + case '\t': + if (isQuoted) { + buffer.append(ch); + break; + } else if (isEscaped) { + buffer.append('\\'); + break loop; + } else { + break loop; + } + + default: + if (isEscaped) { + buffer.append('\\'); + isEscaped = false; + } + + buffer.append(ch); + } + } + + String value = buffer.toString(); + + if (name.length() > 0) { + map.put(name, value); + } + } + + return map; + } + + /** + * Get connection is busy or not + */ + public static boolean pg_connection_busy(Env env, + @NotNull Postgres conn) { + // Always return false, for now (pg_send_xxxx are not asynchronous) + // so there should be no reason for a connection to become busy in + // between different pg_xxx calls. + + return false; + } + + /** + * Reset connection (reconnect) + */ + public static boolean pg_connection_reset(Env env, + @NotNull Postgres conn) { + try { + if (conn == null) { + return false; + } + + // Query database name before closing connection + + String dbname = conn.getDbName(); - ArrayValueImpl newArray = new ArrayValueImpl(); + conn.close(env); - // Keep track of column matches: assocArray vs. table columns - int matches = 0; + conn = new Postgres(env, + conn.getHost(), + conn.getUserName(), + conn.getPassword(), + dbname, + conn.getPort(), + conn.getDriver(), + conn.getUrl()); - while (rs.next()) { - // Retrieve the original value to be converted - String columnName = rs.getString("COLUMN_NAME"); - Value columnNameV = StringValue.create(columnName); - Value value = assocArray.get(columnNameV); + env.setSpecialValue("caucho.postgres", conn); - // Check for column not passed in - if (value == UnsetValue.UNSET) { - continue; + return true; + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return false; + } + } + + /** + * Get connection status + */ + public static int pg_connection_status(Env env, + @NotNull Postgres conn) { + try { + if (conn == null) { + return PGSQL_CONNECTION_BAD; } - matches++; + boolean ping = pg_ping(env, conn); + + return ping ? PGSQL_CONNECTION_OK : PGSQL_CONNECTION_BAD; - if (value.isNull()) { - value = StringValue.create("NULL"); - // Add the converted value - newArray.put(columnNameV, value); - continue; + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return PGSQL_CONNECTION_BAD; + } + } + + /** + * Convert associative array values into suitable for SQL statement + */ + @ReturnNullAsFalse + public static ArrayValue pg_convert(Env env, + @NotNull Postgres conn, + String tableName, + ArrayValue assocArray, + @Optional("0") int options) { + try { + if (conn == null) { + return null; } - // Retrieve the database column type - int dataType = rs.getInt("DATA_TYPE"); + // TODO: options has not been implemented yet. - // Convert the original value to the database type - switch (dataType) { - case Types.BIT: - case Types.TINYINT: - case Types.SMALLINT: - case Types.INTEGER: - case Types.BIGINT: - if (value.isLongConvertible()) { - value = LongValue.create(value.toLong()); - } else { - StringValue sb = env.createUnicodeBuilder(); - value = sb.append("'").append(value).append("'"); - } - break; - - case Types.DECIMAL: - case Types.DOUBLE: - case Types.FLOAT: - case Types.NUMERIC: - case Types.REAL: - if (value.isDoubleConvertible()) { - value = DoubleValue.create(value.toDouble()); - } else { - StringValue sb = env.createUnicodeBuilder(); - value = sb.append("'").append(value).append("'"); - } - break; - - default: - StringValue sb = env.createUnicodeBuilder(); - if (value.isNumberConvertible()) { - value = sb.append(value); - } else { - value = sb.append("'").append(value).append("'"); - } - } - - // Add the converted value - newArray.put(columnNameV, value); - } - - rs.close(); - - // Check if all columns were consumed. Otherwise, there are - // wrong column names passed in. - if (matches < assocArray.getSize()) { + // TODO: the following PHP note has not been implemented yet. + // Note: If there are boolean fields in table_name don't use + // the constant TRUE in assoc_array. It will be converted to the + // string 'TRUE' which is no valid entry for boolean fields in + // PostgreSQL. Use one of t, true, 1, y, yes instead. + + if (options > 0) { + throw new UnimplementedException("pg_convert with options"); + } + + PostgresResult result; + + Connection jdbcConn = conn.getJavaConnection(); + DatabaseMetaData dbMetaData = jdbcConn.getMetaData(); + + ResultSet rs = dbMetaData.getColumns("", "", tableName, ""); + + // Check column count + ResultSetMetaData rsMetaData = rs.getMetaData(); + int n = rsMetaData.getColumnCount(); + if (n < assocArray.getSize()) { + return null; + } + + ArrayValueImpl newArray = new ArrayValueImpl(); + + // Keep track of column matches: assocArray vs. table columns + int matches = 0; + + while (rs.next()) { + // Retrieve the original value to be converted + String columnName = rs.getString("COLUMN_NAME"); + Value columnNameV = StringValue.create(columnName); + Value value = assocArray.get(columnNameV); + + // Check for column not passed in + if (value == UnsetValue.UNSET) { + continue; + } + + matches++; + + if (value.isNull()) { + value = StringValue.create("NULL"); + // Add the converted value + newArray.put(columnNameV, value); + continue; + } + + // Retrieve the database column type + int dataType = rs.getInt("DATA_TYPE"); + + // Convert the original value to the database type + switch (dataType) { + case Types.BIT: + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + if (value.isLongConvertible()) { + value = LongValue.create(value.toLong()); + } else { + StringValue sb = env.createUnicodeBuilder(); + value = sb.append("'").append(value).append("'"); + } + break; + + case Types.DECIMAL: + case Types.DOUBLE: + case Types.FLOAT: + case Types.NUMERIC: + case Types.REAL: + if (value.isDoubleConvertible()) { + value = DoubleValue.create(value.toDouble()); + } else { + StringValue sb = env.createUnicodeBuilder(); + value = sb.append("'").append(value).append("'"); + } + break; + + default: + StringValue sb = env.createUnicodeBuilder(); + if (value.isNumberConvertible()) { + value = sb.append(value); + } else { + value = sb.append("'").append(value).append("'"); + } + } + + // Add the converted value + newArray.put(columnNameV, value); + } + + rs.close(); + + // Check if all columns were consumed. Otherwise, there are + // wrong column names passed in. + if (matches < assocArray.getSize()) { + return null; + } + + return newArray; + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } - - return newArray; - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Insert records into a table from an array - */ - public static boolean pg_copy_from(Env env, - @NotNull Postgres conn, - String tableName, - ArrayValue rows, - @Optional("") String delimiter, - @Optional("") String nullAs) { - // XXX delimiter not used? - - try { - if (conn == null) { - return false; - } - - // TODO: At the time this was implemented, the JDBC driver - // did not support SQL COPY operations that could simplify - // the code below. - - String delimiterRegex; - if (delimiter.equals("")) { - delimiter = "\t"; - delimiterRegex = "\\t"; - } else { - // TODO: even the native php version does not seem to do it very well. - throw new UnimplementedException( - "pg_copy_from with non-default delimiter"); - } - - if (nullAs.equals("")) { - nullAs = "\\N"; - } else { - // TODO: even the native php version does not seem to do it very well. - throw new UnimplementedException( - "pg_copy_from with non-default nullAs"); - } - - ArrayValueImpl array = (ArrayValueImpl) rows; - int size = array.size(); - - String baseInsert = "INSERT INTO " + tableName + " VALUES("; - - StringBuilder sb = new StringBuilder(baseInsert); - - int lenBaseInsert = sb.length(); - - for (int i = 0; i < size; i++) { - // Every line has a new-line '\n' character and - // possibly many NULL values "\\N". Ex: - // line = + } + } + + /** + * Insert records into a table from an array + */ + public static boolean pg_copy_from(Env env, + @NotNull Postgres conn, + String tableName, + ArrayValue rows, + @Optional("") String delimiter, + @Optional("") String nullAs) { + // XXX delimiter not used? + + try { + if (conn == null) { + return false; + } + + // TODO: At the time this was implemented, the JDBC driver + // did not support SQL COPY operations that could simplify + // the code below. + + String delimiterRegex; + if (delimiter.equals("")) { + delimiter = "\t"; + delimiterRegex = "\\t"; + } else { + // TODO: even the native php version does not seem to do it very well. + throw new UnimplementedException( + "pg_copy_from with non-default delimiter"); + } + + if (nullAs.equals("")) { + nullAs = "\\N"; + } else { + // TODO: even the native php version does not seem to do it very well. + throw new UnimplementedException( + "pg_copy_from with non-default nullAs"); + } + + ArrayValueImpl array = (ArrayValueImpl) rows; + int size = array.size(); + + String baseInsert = "INSERT INTO " + tableName + " VALUES("; + + StringBuilder sb = new StringBuilder(baseInsert); + + int lenBaseInsert = sb.length(); + + for (int i = 0; i < size; i++) { + // Every line has a new-line '\n' character and + // possibly many NULL values "\\N". Ex: + // line = // "\\N\tNUMBER1col\t\\N\t\\N\tNUM" + // "BER2col\tNUMBER3col\tNUMBER4col\t\\N\n"; - String line = array.get(LongValue.create(i)).toString(); - line = line.substring(0, line.length() - 1); - - // "INSERT INTO " + tableName + " VALUES(" - sb.setLength(lenBaseInsert); - - // Split columns - String[] cols = line.split(delimiterRegex); - - int len = cols.length; - - if (len > 0) { - - len--; - - for (int j = 0; j < len; j++) { - if (cols[j].equals(nullAs)) { - sb.append("NULL, "); - } else { - sb.append("'"); - sb.append(cols[j]); - sb.append("', "); - } - } - - if (cols[len].equals(nullAs)) { - sb.append("NULL)"); - } else { - sb.append("'"); - sb.append(cols[len]); - sb.append("')"); - } - - // Insert record - pg_query(env, conn, sb.toString()); - } - } - - return true; - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return false; - } - } - - /** - * Copy a table to an array - */ - @ReturnNullAsFalse - public static ArrayValue pg_copy_to(Env env, - @NotNull Postgres conn, - String tableName, - @Optional("") String delimiter, - @Optional("") String nullAs) { - try { - if (conn == null) { - return null; - } + String line = array.get(LongValue.create(i)).toString(); + line = line.substring(0, line.length() - 1); + + // "INSERT INTO " + tableName + " VALUES(" + sb.setLength(lenBaseInsert); + + // Split columns + String[] cols = line.split(delimiterRegex); + + int len = cols.length; + + if (len > 0) { + + len--; + + for (int j = 0; j < len; j++) { + if (cols[j].equals(nullAs)) { + sb.append("NULL, "); + } else { + sb.append("'"); + sb.append(cols[j]); + sb.append("', "); + } + } + + if (cols[len].equals(nullAs)) { + sb.append("NULL)"); + } else { + sb.append("'"); + sb.append(cols[len]); + sb.append("')"); + } + + // Insert record + pg_query(env, conn, sb.toString()); + } + } - // TODO: At the time this was implemented, the JDBC driver - // did not support SQL COPY operations that could simplify - // the code below. + return true; - // TODO: This should be replaced when @Optional("\t") is fixed. - if (delimiter.equals("")) { - delimiter = "\t"; - } + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return false; + } + } + + /** + * Copy a table to an array + */ + @ReturnNullAsFalse + public static ArrayValue pg_copy_to(Env env, + @NotNull Postgres conn, + String tableName, + @Optional("") String delimiter, + @Optional("") String nullAs) { + try { + if (conn == null) { + return null; + } + + // TODO: At the time this was implemented, the JDBC driver + // did not support SQL COPY operations that could simplify + // the code below. + + // TODO: This should be replaced when @Optional("\t") is fixed. + if (delimiter.equals("")) { + delimiter = "\t"; + } + + // TODO: This should be replaced when @Optional("\\N") is fixed. + // Note: according to php.net, it must be \\N, i.e. the + // two-character sequence: {'\\', 'N'} + if (nullAs.equals("")) { + nullAs = "\\N"; + } + + PostgresResult result = pg_query(env, conn, "SELECT * FROM " + tableName); - // TODO: This should be replaced when @Optional("\\N") is fixed. - // Note: according to php.net, it must be \\N, i.e. the - // two-character sequence: {'\\', 'N'} - if (nullAs.equals("")) { - nullAs = "\\N"; - } + ArrayValueImpl newArray = new ArrayValueImpl(); - PostgresResult result = pg_query(env, conn, "SELECT * FROM " + tableName); + Object value; - ArrayValueImpl newArray = new ArrayValueImpl(); + int curr = 0; - Object value; + while ((value = result.fetchArray(env, PGSQL_NUM)) != null) { - int curr = 0; + ArrayValueImpl arr = (ArrayValueImpl) value; + int count = arr.size(); - while ((value = result.fetchArray(env, PGSQL_NUM)) != null) { + StringValue sb = env.createUnicodeBuilder(); - ArrayValueImpl arr = (ArrayValueImpl) value; - int count = arr.size(); + LongValue currValue = LongValue.create(curr); - StringValue sb = env.createUnicodeBuilder(); + for (int i = 0; i < count; i++) { - LongValue currValue = LongValue.create(curr); + if (sb.length() > 0) { + sb.append(delimiter); + } - for (int i = 0; i < count; i++) { + Value v = newArray.get(currValue); - if (sb.length() > 0) { - sb.append(delimiter); - } + Value fieldValue = arr.get(LongValue.create(i)); - Value v = newArray.get(currValue); + if (fieldValue instanceof NullValue) { + sb.append(nullAs); + } else { + sb.append(fieldValue.toString()); + } + } - Value fieldValue = arr.get(LongValue.create(i)); + // Every line has a new-line character. + sb.append("\n"); - if (fieldValue instanceof NullValue) { - sb.append(nullAs); - } else { - sb.append(fieldValue.toString()); - } + newArray.put(currValue, sb); + + curr++; } - // Every line has a new-line character. - sb.append("\n"); + return newArray; - newArray.put(currValue, sb); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } + + /** + * Get the database name + */ + @ReturnNullAsFalse + public static String pg_dbname(Env env, + @Optional Postgres conn) { + try { + if (conn == null) { + conn = getConnection(env); + } - curr++; - } + return conn.getDbName(); - return newArray; + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } + + /** + * Deletes records + */ + public static boolean pg_delete(Env env, + @NotNull Postgres conn, + String tableName, + ArrayValue assocArray, + @Optional("-1") int options) { + // From php.net: this function is EXPERIMENTAL. + // This function is EXPERIMENTAL. The behaviour of this function, + // the name of this function, and anything else + // documented about this function + // may change without notice in a future release of PHP. + // Use this function at your own risk. + + try { + if (conn == null) { + return false; + } - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } + if (options > 0) { + throw new UnimplementedException("pg_delete with options"); + } - /** - * Get the database name - */ - @ReturnNullAsFalse - public static String pg_dbname(Env env, - @Optional Postgres conn) { - try { - if (conn == null) { - conn = getConnection(env); - } - - return conn.getDbName(); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Deletes records - */ - public static boolean pg_delete(Env env, - @NotNull Postgres conn, - String tableName, - ArrayValue assocArray, - @Optional("-1") int options) { - // From php.net: this function is EXPERIMENTAL. - // This function is EXPERIMENTAL. The behaviour of this function, - // the name of this function, and anything else - // documented about this function - // may change without notice in a future release of PHP. - // Use this function at your own risk. - - try { - if (conn == null) { + StringBuilder condition = new StringBuilder(); + + boolean isFirst = true; + + for (Map.Entry entry : assocArray.entrySet()) { + Value k = entry.getKey(); + Value v = entry.getValue(); + if (isFirst) { + isFirst = false; + } else { + condition.append(" AND "); + } + condition.append(k.toString()); + condition.append("='"); + condition.append(v.toString()); + condition.append("'"); + } + + StringBuilder query = new StringBuilder(); + query.append("DELETE FROM "); + query.append(tableName); + query.append(" WHERE "); + query.append(condition); + + pg_query(env, conn, query.toString()); + + return true; + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return false; - } + } + } - if (options > 0) { - throw new UnimplementedException("pg_delete with options"); - } + /** + * Sync with PostgreSQL backend + */ + public static boolean pg_end_copy(Env env, + @Optional Postgres conn) { + env.stub("pg_end_copy"); - StringBuilder condition = new StringBuilder(); + return false; + } - boolean isFirst = true; + /** + * Escape a string for insertion into a bytea field + */ + @ReturnNullAsFalse + public static StringValue pg_escape_bytea(Env env, + StringValue data) { + if (data.length() == 0) { + return data; + } - for (Map.Entry entry : assocArray.entrySet()) { - Value k = entry.getKey(); - Value v = entry.getValue(); - if (isFirst) { - isFirst = false; - } else { - condition.append(" AND "); - } - condition.append(k.toString()); - condition.append("='"); - condition.append(v.toString()); - condition.append("'"); - } - - StringBuilder query = new StringBuilder(); - query.append("DELETE FROM "); - query.append(tableName); - query.append(" WHERE "); - query.append(condition); - - pg_query(env, conn, query.toString()); - - return true; - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return false; - } - } - - /** - * Sync with PostgreSQL backend - */ - public static boolean pg_end_copy(Env env, - @Optional Postgres conn) { - env.stub("pg_end_copy"); - - return false; - } - - /** - * Escape a string for insertion into a bytea field - */ - @ReturnNullAsFalse - public static StringValue pg_escape_bytea(Env env, - StringValue data) { - if (data.length() == 0) { - return data; - } - - try { - Class cl = Class.forName("org.postgresql.util.PGbytea"); - - Method method = cl.getDeclaredMethod( - "toPGString", new Class[]{byte[].class}); - - String s = (String) method.invoke(cl, new Object[]{data.toBytes()}); - - return Postgres.pgRealEscapeString(env.createString(s)); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Escape a string for insertion into a bytea field. - */ - @ReturnNullAsFalse - public static StringValue pg_escape_bytea(Env env, - @NotNull Postgres conn, - StringValue data) { - return pg_escape_bytea(env, data); - } - - /** - * Escape a string for insertion into a text field - */ - @ReturnNullAsFalse - public static StringValue pg_escape_string(Env env, - StringValue data) { - try { - Postgres conn = getConnection(env); - - if (conn == null) { + try { + Class cl = Class.forName("org.postgresql.util.PGbytea"); + + Method method = cl.getDeclaredMethod( + "toPGString", new Class[]{byte[].class}); + + String s = (String) method.invoke(cl, new Object[]{data.toBytes()}); + + return Postgres.pgRealEscapeString(env.createString(s)); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } - - return conn.realEscapeString(data); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Escape a string for insertion into a text field - */ - @ReturnNullAsFalse - public static StringValue pg_escape_string(Env env, - @NotNull Postgres conn, - StringValue data) { - try { - return conn.realEscapeString(data); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Sends a request to execute a prepared statement with given parameters, - * and waits for the result - */ - @ReturnNullAsFalse - public static PostgresResult pg_execute(Env env, - @NotNull Postgres conn, - String stmtName, - ArrayValue params) { - try { - if (conn == null) { + } + } + + /** + * Escape a string for insertion into a bytea field. + */ + @ReturnNullAsFalse + public static StringValue pg_escape_bytea(Env env, + @NotNull Postgres conn, + StringValue data) { + return pg_escape_bytea(env, data); + } + + /** + * Escape a string for insertion into a text field + */ + @ReturnNullAsFalse + public static StringValue pg_escape_string(Env env, + StringValue data) { + try { + Postgres conn = getConnection(env); + + if (conn == null) { + return null; + } + + return conn.realEscapeString(data); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } - - PostgresStatement pstmt = conn.getStatement(stmtName); - - return executeInternal(env, conn, pstmt, params); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - conn.setResultResource(null); - return null; - } - } - - /** - * Fetches all rows in a particular result column as an array - */ - @ReturnNullAsFalse - public static ArrayValue pg_fetch_all_columns(Env env, - @NotNull PostgresResult result, - @Optional("0") int column) { - try { - if (result == null) { + } + } + + /** + * Escape a string for insertion into a text field + */ + @ReturnNullAsFalse + public static StringValue pg_escape_string(Env env, + @NotNull Postgres conn, + StringValue data) { + try { + return conn.realEscapeString(data); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } + } + } + + /** + * Sends a request to execute a prepared statement with given parameters, + * and waits for the result + */ + @ReturnNullAsFalse + public static PostgresResult pg_execute(Env env, + @NotNull Postgres conn, + String stmtName, + ArrayValue params) { + try { + if (conn == null) { + return null; + } - ArrayValueImpl newArray = new ArrayValueImpl(); + PostgresStatement pstmt = conn.getStatement(stmtName); - int curr = 0; + return executeInternal(env, conn, pstmt, params); - for (ArrayValue row = result.fetchRow(env); - row != null; - row = result.fetchRow(env)) { + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + conn.setResultResource(null); + return null; + } + } + + /** + * Fetches all rows in a particular result column as an array + */ + @ReturnNullAsFalse + public static ArrayValue pg_fetch_all_columns(Env env, + @NotNull PostgresResult result, + @Optional("0") int column) { + try { + if (result == null) { + return null; + } - newArray.put(LongValue.create(curr++), - row.get(LongValue.create(column))); + ArrayValueImpl newArray = new ArrayValueImpl(); - } + int curr = 0; - if (newArray.getSize() > 0) { - return newArray; - } - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - } - - return null; - } - - /** - * Fetches all rows from a result as an array - */ - @ReturnNullAsFalse - public static ArrayValue pg_fetch_all(Env env, - @NotNull PostgresResult result) { - try { - if (result == null) { - return null; - } + for (ArrayValue row = result.fetchRow(env); + row != null; + row = result.fetchRow(env)) { - ArrayValueImpl newArray = new ArrayValueImpl(); + newArray.put(LongValue.create(curr++), + row.get(LongValue.create(column))); - int curr = 0; + } - for (ArrayValue row = result.fetchAssoc(env); - row != null; - row = result.fetchAssoc(env)) { + if (newArray.getSize() > 0) { + return newArray; + } - newArray.put(LongValue.create(curr++), row); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + } + + return null; + } + + /** + * Fetches all rows from a result as an array + */ + @ReturnNullAsFalse + public static ArrayValue pg_fetch_all(Env env, + @NotNull PostgresResult result) { + try { + if (result == null) { + return null; + } - } + ArrayValueImpl newArray = new ArrayValueImpl(); - if (newArray.getSize() > 0) { - return newArray; - } - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - } - - return null; - } - - /** - * Fetch a row as an array - */ - @ReturnNullAsFalse - public static ArrayValue pg_fetch_array( - Env env, - @NotNull PostgresResult result, - @Optional("-1") Value row, - @Optional("PGSQL_BOTH") int resultType) { - try { - if (result == null) { - return null; - } - - // NOTE: pg_fetch_array has an interesting memory feature. - // Calls to pg_fetch_array usually return the next row for - // successive calls. There is an exception though. - // The first time a NULL row is passed in, the previously - // returned row is returned again. After that, successive - // calls return the next row as usual. - // We set a flag for this. See PostgresResult and php/4342 - - if (row.isNull()) { - if (result.getPassedNullRow()) { - result.setPassedNullRow(); - } else { - // Step the cursor back to the previous position - ResultSet rs = result.getResultSet(); - rs.previous(); - } - } - - // NOTE: row is of type Value because row is optional and there is - // only one way to specify that 'row' will not be used: - // - // pg_fetch_array(result, NULL, resultType) - // - // The resultType will be used above though. - // - // For such a case, the marshalling code passes row in as NullValue.NULL - // If we used 'int row' there would be no way to distinguish row 'zero' - // from row 'null'. - - if (result == null) { - return null; - } - - if (row.isLongConvertible() && row.toInt() >= 0) { - if (!result.seek(env, row.toInt())) { - env.warning(L.l("Unable to jump to row {0} on PostgreSQL result", - row.toInt())); - return null; - } - } - - return result.fetchArray(env, resultType); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Fetch a row as an associative array - */ - @ReturnNullAsFalse - public static ArrayValue pg_fetch_assoc(Env env, - @NotNull PostgresResult result, - @Optional("-1") Value row) { - try { - if (result == null) { + int curr = 0; + + for (ArrayValue row = result.fetchAssoc(env); + row != null; + row = result.fetchAssoc(env)) { + + newArray.put(LongValue.create(curr++), row); + + } + + if (newArray.getSize() > 0) { + return newArray; + } + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + } + + return null; + } + + /** + * Fetch a row as an array + */ + @ReturnNullAsFalse + public static ArrayValue pg_fetch_array( + Env env, + @NotNull PostgresResult result, + @Optional("-1") Value row, + @Optional("PGSQL_BOTH") int resultType) { + try { + if (result == null) { + return null; + } + + // NOTE: pg_fetch_array has an interesting memory feature. + // Calls to pg_fetch_array usually return the next row for + // successive calls. There is an exception though. + // The first time a NULL row is passed in, the previously + // returned row is returned again. After that, successive + // calls return the next row as usual. + // We set a flag for this. See PostgresResult and php/4342 + + if (row.isNull()) { + if (result.getPassedNullRow()) { + result.setPassedNullRow(); + } else { + // Step the cursor back to the previous position + ResultSet rs = result.getResultSet(); + rs.previous(); + } + } + + // NOTE: row is of type Value because row is optional and there is + // only one way to specify that 'row' will not be used: + // + // pg_fetch_array(result, NULL, resultType) + // + // The resultType will be used above though. + // + // For such a case, the marshalling code passes row in as NullValue.NULL + // If we used 'int row' there would be no way to distinguish row 'zero' + // from row 'null'. + + if (result == null) { + return null; + } + + if (row.isLongConvertible() && row.toInt() >= 0) { + if (!result.seek(env, row.toInt())) { + env.warning(L.l("Unable to jump to row {0} on PostgreSQL result", + row.toInt())); + return null; + } + } + + return result.fetchArray(env, resultType); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } - - if (!row.isNull() && row.toInt() >= 0) { - result.seek(env, row.toInt()); - } - - return result.fetchAssoc(env); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Fetch a row as an object - */ - public static Value pg_fetch_object(Env env, - @NotNull PostgresResult result, - @Optional("-1") Value row, - @Optional int resultType) { - try { - if (result == null) { + } + } + + /** + * Fetch a row as an associative array + */ + @ReturnNullAsFalse + public static ArrayValue pg_fetch_assoc(Env env, + @NotNull PostgresResult result, + @Optional("-1") Value row) { + try { + if (result == null) { + return null; + } + + if (!row.isNull() && row.toInt() >= 0) { + result.seek(env, row.toInt()); + } + + return result.fetchAssoc(env); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } + } + } + + /** + * Fetch a row as an object + */ + public static Value pg_fetch_object(Env env, + @NotNull PostgresResult result, + @Optional("-1") Value row, + @Optional int resultType) { + try { + if (result == null) { + return null; + } - //@todo use optional resultType - if ((row != null) && (!row.equals(NullValue.NULL)) - && (row.toInt() >= 0)) { - result.seek(env, row.toInt()); - } + //@todo use optional resultType + if ((row != null) && (!row.equals(NullValue.NULL)) + && (row.toInt() >= 0)) { + result.seek(env, row.toInt()); + } - Value resultValue = result.fetchObject(env); + Value resultValue = result.fetchObject(env); - // php/430l - if (resultValue.isNull()) { + // php/430l + if (resultValue.isNull()) { + return BooleanValue.FALSE; + } else { + return resultValue; + } + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return BooleanValue.FALSE; - } else { - return resultValue; - } - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return BooleanValue.FALSE; - } - } - - /** - * Returns values from a result resource - */ - public static Value pg_fetch_result(Env env, - @NotNull PostgresResult result, - Value row, - @Optional("-1") Value fieldNameOrNumber) { - try { - if (result == null) { - return null; - } - - // NOTE: row is of type Value because there is a case where - // row is optional. In such a case, the row value passed in - // is actually the field number or field name. - - int rowNumber = -1; - - // Handle the case: optional row with mandatory fieldNameOrNumber. - if (fieldNameOrNumber.isLongConvertible() - && (fieldNameOrNumber.toInt() < 0)) { - fieldNameOrNumber = row; - rowNumber = -1; - } else { - rowNumber = row.toInt(); - } - - if (rowNumber >= 0) { - result.seek(env, rowNumber); - } - - Value fetchRow = result.fetchRow(env); - - int fieldNumber = result.getColumnNumber(fieldNameOrNumber, 0); - - return fetchRow.get(LongValue.create(fieldNumber)); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return BooleanValue.FALSE; - } - } - - /** - * Returns values from a result resource - */ - public static Value pg_result(Env env, - @NotNull PostgresResult result, - Value row, - @Optional("-1") Value fieldNameOrNumber) { - return pg_fetch_result(env, result, row, fieldNameOrNumber); - } - - /** - * Get a row as an enumerated array - */ - @ReturnNullAsFalse - public static ArrayValue pg_fetch_row(Env env, - @NotNull PostgresResult result, - @Optional("-1") Value row) { - try { - if (result == null) { + } + } + + /** + * Returns values from a result resource + */ + public static Value pg_fetch_result(Env env, + @NotNull PostgresResult result, + Value row, + @Optional("-1") Value fieldNameOrNumber) { + try { + if (result == null) { + return null; + } + + // NOTE: row is of type Value because there is a case where + // row is optional. In such a case, the row value passed in + // is actually the field number or field name. + + int rowNumber = -1; + + // Handle the case: optional row with mandatory fieldNameOrNumber. + if (fieldNameOrNumber.isLongConvertible() + && (fieldNameOrNumber.toInt() < 0)) { + fieldNameOrNumber = row; + rowNumber = -1; + } else { + rowNumber = row.toInt(); + } + + if (rowNumber >= 0) { + result.seek(env, rowNumber); + } + + Value fetchRow = result.fetchRow(env); + + int fieldNumber = result.getColumnNumber(fieldNameOrNumber, 0); + + return fetchRow.get(LongValue.create(fieldNumber)); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return BooleanValue.FALSE; + } + } + + /** + * Returns values from a result resource + */ + public static Value pg_result(Env env, + @NotNull PostgresResult result, + Value row, + @Optional("-1") Value fieldNameOrNumber) { + return pg_fetch_result(env, result, row, fieldNameOrNumber); + } + + /** + * Get a row as an enumerated array + */ + @ReturnNullAsFalse + public static ArrayValue pg_fetch_row(Env env, + @NotNull PostgresResult result, + @Optional("-1") Value row) { + try { + if (result == null) { + return null; + } + + if (row != null && !row.equals(NullValue.NULL) && row.toInt() >= 0) { + result.seek(env, row.toInt()); + } + + return result.fetchRow(env); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } - - if (row != null && !row.equals(NullValue.NULL) && row.toInt() >= 0) { - result.seek(env, row.toInt()); - } - - return result.fetchRow(env); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Test if a field is SQL NULL - */ - @ReturnNullAsFalse - public static LongValue pg_field_is_null( - Env env, - @NotNull PostgresResult result, - Value row, - @Optional("-1") Value fieldNameOrNumber) { - try { - if (result == null) { + } + } + + /** + * Test if a field is SQL NULL + */ + @ReturnNullAsFalse + public static LongValue pg_field_is_null( + Env env, + @NotNull PostgresResult result, + Value row, + @Optional("-1") Value fieldNameOrNumber) { + try { + if (result == null) { + return null; + } + + // NOTE: row is of type Value because there is a case where + // row is optional. In such a case, the row value passed in + // is actually the field number or field name. + + int rowNumber = -1; + + // Handle the case: optional row with mandatory fieldNameOrNumber. + if (fieldNameOrNumber.isLongConvertible() + && (fieldNameOrNumber.toInt() == -1)) { + fieldNameOrNumber = row; + rowNumber = -1; + } else { + rowNumber = row.toInt(); + } + + if (rowNumber >= 0) { + if (!result.seek(env, rowNumber)) { + env.warning(L.l("Unable to jump to row {0} on PostgreSQL result", + rowNumber)); + return null; + } + } + + int fieldNumber = result.getColumnNumber(fieldNameOrNumber, 0); + + Value field = pg_fetch_result(env, + result, + LongValue.MINUS_ONE, + LongValue.create(fieldNumber)); + + if (field == null || field.isNull()) { + return LongValue.ONE; + } else { + return LongValue.ZERO; + } + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } - - // NOTE: row is of type Value because there is a case where - // row is optional. In such a case, the row value passed in - // is actually the field number or field name. - - int rowNumber = -1; - - // Handle the case: optional row with mandatory fieldNameOrNumber. - if (fieldNameOrNumber.isLongConvertible() - && (fieldNameOrNumber.toInt() == -1)) { - fieldNameOrNumber = row; - rowNumber = -1; - } else { - rowNumber = row.toInt(); - } - - if (rowNumber >= 0) { - if (!result.seek(env, rowNumber)) { - env.warning(L.l("Unable to jump to row {0} on PostgreSQL result", - rowNumber)); - return null; - } - } - - int fieldNumber = result.getColumnNumber(fieldNameOrNumber, 0); - - Value field = pg_fetch_result(env, - result, - LongValue.MINUS_ONE, - LongValue.create(fieldNumber)); - - if (field == null || field.isNull()) { - return LongValue.ONE; - } else { - return LongValue.ZERO; - } - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * pg_field_is_null() alias. - */ - @ReturnNullAsFalse - public static LongValue pg_fieldisnull( - Env env, - @NotNull PostgresResult result, - Value row, - @Optional("-1") Value fieldNameOrNumber) { - return pg_field_is_null(env, result, row, fieldNameOrNumber); - } - - /** - * Returns the name of a field - */ - public static Value pg_field_name(Env env, - @NotNull PostgresResult result, - int fieldNumber) { - try { - if (result == null) { + } + } + + /** + * pg_field_is_null() alias. + */ + @ReturnNullAsFalse + public static LongValue pg_fieldisnull( + Env env, + @NotNull PostgresResult result, + Value row, + @Optional("-1") Value fieldNameOrNumber) { + return pg_field_is_null(env, result, row, fieldNameOrNumber); + } + + /** + * Returns the name of a field + */ + public static Value pg_field_name(Env env, + @NotNull PostgresResult result, + int fieldNumber) { + try { + if (result == null) { + return BooleanValue.FALSE; + } + + return result.getFieldName(env, fieldNumber); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return BooleanValue.FALSE; - } - - return result.getFieldName(env, fieldNumber); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return BooleanValue.FALSE; - } - } - - /** - * pg_field_name() alias. - */ - public static Value pg_fieldname(Env env, - @NotNull PostgresResult result, - int fieldNumber) { - return pg_field_name(env, result, fieldNumber); - } - - /** - * Returns the field number of the named field - * - * @return the field number (0-based) or -1 on error - */ - public static int pg_field_num(Env env, - @NotNull PostgresResult result, - String fieldName) { - try { - if (result == null) { + } + } + + /** + * pg_field_name() alias. + */ + public static Value pg_fieldname(Env env, + @NotNull PostgresResult result, + int fieldNumber) { + return pg_field_name(env, result, fieldNumber); + } + + /** + * Returns the field number of the named field + * + * @return the field number (0-based) or -1 on error + */ + public static int pg_field_num(Env env, + @NotNull PostgresResult result, + String fieldName) { + try { + if (result == null) { + return -1; + } + + return result.getColumnNumber(fieldName); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return -1; - } - - return result.getColumnNumber(fieldName); - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return -1; - } - } - - /** - * pg_field_num() alias. - */ - public static int pg_fieldnum(Env env, - @NotNull PostgresResult result, - String fieldName) { - return pg_field_num(env, result, fieldName); - } - - /** - * Returns the printed length - */ - public static int pg_field_prtlen(Env env, - @NotNull PostgresResult result, - Value rowNumber, - @Optional("-1") Value fieldNameOrNumber) { - try { - if (result == null) { + } + } + + /** + * pg_field_num() alias. + */ + public static int pg_fieldnum(Env env, + @NotNull PostgresResult result, + String fieldName) { + return pg_field_num(env, result, fieldName); + } + + /** + * Returns the printed length + */ + public static int pg_field_prtlen(Env env, + @NotNull PostgresResult result, + Value rowNumber, + @Optional("-1") Value fieldNameOrNumber) { + try { + if (result == null) { + return -1; + } + + int row = rowNumber.toInt(); + + if (fieldNameOrNumber.toString().equals("-1")) { + fieldNameOrNumber = rowNumber; + row = -1; + } + + int fieldNumber = result.getColumnNumber(fieldNameOrNumber, 0); + + ResultSetMetaData metaData = result.getMetaData(); + String typeName = metaData.getColumnTypeName(fieldNumber + 1); + if (typeName.equals("bool")) { + return 1; + } + + Value value = pg_fetch_result(env, + result, + LongValue.create(row), + LongValue.create(fieldNumber)); + + // Step the cursor back to the original position + // See php/430p + result.getResultSet().previous(); + + int len = value.toString().length(); + + // TODO: check this... + // if (typeName.equals("money")) { + // len++; + // } + + return len; + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return -1; - } - - int row = rowNumber.toInt(); - - if (fieldNameOrNumber.toString().equals("-1")) { - fieldNameOrNumber = rowNumber; - row = -1; - } - - int fieldNumber = result.getColumnNumber(fieldNameOrNumber, 0); - - ResultSetMetaData metaData = result.getMetaData(); - String typeName = metaData.getColumnTypeName(fieldNumber + 1); - if (typeName.equals("bool")) { - return 1; - } - - Value value = pg_fetch_result(env, - result, - LongValue.create(row), - LongValue.create(fieldNumber)); - - // Step the cursor back to the original position - // See php/430p - result.getResultSet().previous(); - - int len = value.toString().length(); - - // TODO: check this... - // if (typeName.equals("money")) { - // len++; - // } - - return len; - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return -1; - } - } - - /** - * pg_field_ptrlen() alias. - */ - public static int pg_fieldprtlen(Env env, - @NotNull PostgresResult result, - Value rowNumber, - @Optional("-1") Value fieldNameOrNumber) { - return pg_field_prtlen(env, result, rowNumber, fieldNameOrNumber); - } - - /** - * Returns the internal storage size of the named field - */ - @ReturnNullAsFalse - public static LongValue pg_field_size(Env env, - @NotNull PostgresResult result, - int fieldNumber) { - try { - if (result == null) { - return LongValue.create(-1); - } - - ResultSetMetaData metaData = result.getMetaData(); - - fieldNumber++; - - int dataType = metaData.getColumnType(fieldNumber); - - int size = -1; - - switch (dataType) { - case Types.BIT: { - String typeName = metaData.getColumnTypeName(fieldNumber); - if (typeName.equals("bool")) { - size = 1; - } - break; - } - - case Types.TINYINT: - size = 1; - break; - - case Types.SMALLINT: - size = 2; - break; - - case Types.DATE: - case Types.FLOAT: - case Types.INTEGER: - case Types.REAL: - size = 4; - break; - - case Types.BIGINT: - case Types.DOUBLE: { - size = 8; - String typeName = metaData.getColumnTypeName(fieldNumber); - if (typeName.equals("money")) { - size = 4; - } - } - break; - - case Types.TIME: - case Types.TIMESTAMP: - size = 8; - // fall to specific cases - - default: { - String typeName = metaData.getColumnTypeName(fieldNumber); - if (typeName.equals("timetz") - || typeName.equals("interval")) { - size = 12; - } else if (typeName.equals("macaddr")) { - size = 6; - } else if (typeName.equals("point")) { - size = 16; - } else if (typeName.equals("circle")) { - size = 24; - } else if (typeName.equals("box") - || typeName.equals("lseg")) { - size = 32; - } - } - } - - return LongValue.create(size); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * pg_field_size() alias. - */ - @ReturnNullAsFalse - public static LongValue pg_fieldsize(Env env, - @NotNull PostgresResult result, - int fieldNumber) { - return pg_field_size(env, result, fieldNumber); - } - - /** - * Returns the name or oid of the tables field - * - * @return By default the tables name that field belongs to - * is returned but if oid_only is set to TRUE, - * then the oid will instead be returned. - */ - @ReturnNullAsFalse - public static String pg_field_table(Env env, - @NotNull PostgresResult result, - int fieldNumber, - @Optional("false") boolean oidOnly) { - // The Postgres JDBC driver doesn't have a concept of exposing - // to the client what table maps to a particular select item - // in a result set, therefore the driver cannot report anything - // useful to the caller. Thus the driver always returns "" to - // ResultSetMetaData.getTableName(fieldNumber+1) - - env.stub("pg_field_table"); - - return ""; - } - - /** - * Returns the type ID (OID) for the corresponding field number - */ - @ReturnNullAsFalse - public static LongValue pg_field_type_oid(Env env, - @NotNull PostgresResult result, - int fieldNumber) { - try { - if (result == null) { + } + } + + /** + * pg_field_ptrlen() alias. + */ + public static int pg_fieldprtlen(Env env, + @NotNull PostgresResult result, + Value rowNumber, + @Optional("-1") Value fieldNameOrNumber) { + return pg_field_prtlen(env, result, rowNumber, fieldNameOrNumber); + } + + /** + * Returns the internal storage size of the named field + */ + @ReturnNullAsFalse + public static LongValue pg_field_size(Env env, + @NotNull PostgresResult result, + int fieldNumber) { + try { + if (result == null) { + return LongValue.create(-1); + } + + ResultSetMetaData metaData = result.getMetaData(); + + fieldNumber++; + + int dataType = metaData.getColumnType(fieldNumber); + + int size = -1; + + switch (dataType) { + case Types.BIT: { + String typeName = metaData.getColumnTypeName(fieldNumber); + if (typeName.equals("bool")) { + size = 1; + } + break; + } + + case Types.TINYINT: + size = 1; + break; + + case Types.SMALLINT: + size = 2; + break; + + case Types.DATE: + case Types.FLOAT: + case Types.INTEGER: + case Types.REAL: + size = 4; + break; + + case Types.BIGINT: + case Types.DOUBLE: { + size = 8; + String typeName = metaData.getColumnTypeName(fieldNumber); + if (typeName.equals("money")) { + size = 4; + } + } + break; + + case Types.TIME: + case Types.TIMESTAMP: + size = 8; + // fall to specific cases + + default: { + String typeName = metaData.getColumnTypeName(fieldNumber); + if (typeName.equals("timetz") + || typeName.equals("interval")) { + size = 12; + } else if (typeName.equals("macaddr")) { + size = 6; + } else if (typeName.equals("point")) { + size = 16; + } else if (typeName.equals("circle")) { + size = 24; + } else if (typeName.equals("box") + || typeName.equals("lseg")) { + size = 32; + } + } + } + + return LongValue.create(size); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } + } + } + + /** + * pg_field_size() alias. + */ + @ReturnNullAsFalse + public static LongValue pg_fieldsize(Env env, + @NotNull PostgresResult result, + int fieldNumber) { + return pg_field_size(env, result, fieldNumber); + } + + /** + * Returns the name or oid of the tables field + * + * @return By default the tables name that field belongs to + * is returned but if oid_only is set to TRUE, + * then the oid will instead be returned. + */ + @ReturnNullAsFalse + public static String pg_field_table(Env env, + @NotNull PostgresResult result, + int fieldNumber, + @Optional("false") boolean oidOnly) { + // The Postgres JDBC driver doesn't have a concept of exposing + // to the client what table maps to a particular select item + // in a result set, therefore the driver cannot report anything + // useful to the caller. Thus the driver always returns "" to + // ResultSetMetaData.getTableName(fieldNumber+1) + + env.stub("pg_field_table"); + + return ""; + } + + /** + * Returns the type ID (OID) for the corresponding field number + */ + @ReturnNullAsFalse + public static LongValue pg_field_type_oid(Env env, + @NotNull PostgresResult result, + int fieldNumber) { + try { + if (result == null) { + return null; + } + + ResultSetMetaData metaData = result.getMetaData(); + + String columnTypeName = metaData.getColumnTypeName(fieldNumber + 1); - ResultSetMetaData metaData = result.getMetaData(); + String metaQuery = + ("SELECT oid FROM pg_type WHERE typname='" + columnTypeName + "'"); - String columnTypeName = metaData.getColumnTypeName(fieldNumber + 1); + result = pg_query(env, (Postgres) result.getConnection(), metaQuery); - String metaQuery = - ("SELECT oid FROM pg_type WHERE typname='" + columnTypeName + "'"); + Value value = pg_fetch_result(env, + result, + LongValue.MINUS_ONE, + LongValue.ZERO); - result = pg_query(env, (Postgres) result.getConnection(), metaQuery); + if (value.isLongConvertible()) { + return LongValue.create(value.toLong()); + } + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + } + + return null; + } + + /** + * Returns the type name for the corresponding field number + */ + @ReturnNullAsFalse + public static StringValue pg_field_type(Env env, + @NotNull PostgresResult result, + int fieldNumber) { + try { + if (result == null) { + return null; + } - Value value = pg_fetch_result(env, - result, - LongValue.MINUS_ONE, - LongValue.ZERO); + ResultSetMetaData metaData = result.getMetaData(); - if (value.isLongConvertible()) { - return LongValue.create(value.toLong()); - } + fieldNumber++; - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - } + String typeName = metaData.getColumnTypeName(fieldNumber); - return null; - } + return (StringValue) StringValue.create(typeName); - /** - * Returns the type name for the corresponding field number - */ - @ReturnNullAsFalse - public static StringValue pg_field_type(Env env, - @NotNull PostgresResult result, - int fieldNumber) { - try { - if (result == null) { + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } - - ResultSetMetaData metaData = result.getMetaData(); - - fieldNumber++; - - String typeName = metaData.getColumnTypeName(fieldNumber); - - return (StringValue) StringValue.create(typeName); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * pg_field_type() alias. - */ - @ReturnNullAsFalse - public static StringValue pg_fieldtype(Env env, - @NotNull PostgresResult result, - int fieldNumber) { - return pg_field_type(env, result, fieldNumber); - } - - /** - * Free result memory - */ - public static boolean pg_free_result(Env env, - PostgresResult result) { - try { - if (result == null) { + } + } + + /** + * pg_field_type() alias. + */ + @ReturnNullAsFalse + public static StringValue pg_fieldtype(Env env, + @NotNull PostgresResult result, + int fieldNumber) { + return pg_field_type(env, result, fieldNumber); + } + + /** + * Free result memory + */ + public static boolean pg_free_result(Env env, + PostgresResult result) { + try { + if (result == null) { + return true; + } + + result.close(); + return true; - } - - result.close(); - - return true; - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - - return true; - } - } - - /** - * pg_free_result() alias. - */ - public static boolean pg_freeresult(Env env, - PostgresResult result) { - if (result == null) { - return true; - } - - return pg_free_result(env, result); - } - - /** - * Gets SQL NOTIFY message - */ - @ReturnNullAsFalse - public static ArrayValue pg_get_notify(Env env, - @NotNull Postgres conn, - @Optional("-1") int resultType) { - try { - if (conn == null) { - return null; - } + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + + return true; + } + } + + /** + * pg_free_result() alias. + */ + public static boolean pg_freeresult(Env env, + PostgresResult result) { + if (result == null) { + return true; + } + + return pg_free_result(env, result); + } + + /** + * Gets SQL NOTIFY message + */ + @ReturnNullAsFalse + public static ArrayValue pg_get_notify(Env env, + @NotNull Postgres conn, + @Optional("-1") int resultType) { + try { + if (conn == null) { + return null; + } - if (resultType > 0) { - throw new UnimplementedException("pg_get_notify with result type"); - } + if (resultType > 0) { + throw new UnimplementedException("pg_get_notify with result type"); + } - // org.postgresql.PGConnection - Class cl = Class.forName("org.postgresql.PGConnection"); + // org.postgresql.PGConnection + Class cl = Class.forName("org.postgresql.PGConnection"); - // public PGNotification[] getNotifications() throws SQLException; - Method method = cl.getDeclaredMethod("getNotifications", (Class) null); + // public PGNotification[] getNotifications() throws SQLException; + Method method = cl.getDeclaredMethod("getNotifications", (Class) null); - Connection pgconn = conn.getJavaConnection(); + Connection pgconn = conn.getJavaConnection(); - // getNotifications() - Object[] notifications = (Object[]) method.invoke(pgconn, new Object[]{}); + // getNotifications() + Object[] notifications = (Object[]) method.invoke(pgconn, new Object[]{}); - // org.postgresql.PGNotification - cl = Class.forName("org.postgresql.PGNotification"); + // org.postgresql.PGNotification + cl = Class.forName("org.postgresql.PGNotification"); - // public String getName(); - Method methodGetName = cl.getDeclaredMethod("getName", (Class) null); + // public String getName(); + Method methodGetName = cl.getDeclaredMethod("getName", (Class) null); - // public int getPID(); - Method methodGetPID = cl.getDeclaredMethod("getPID", (Class) null); + // public int getPID(); + Method methodGetPID = cl.getDeclaredMethod("getPID", (Class) null); - ArrayValueImpl arrayValue = new ArrayValueImpl(); + ArrayValueImpl arrayValue = new ArrayValueImpl(); - int n = notifications.length; + int n = notifications.length; - StringValue k; - LongValue v; + StringValue k; + LongValue v; - for (int i = 0; i < n; i++) { - // getName() - k = (StringValue) StringValue.create( - methodGetName.invoke(notifications[i], - new Object[]{})); - // getPID() - v = (LongValue) LongValue.create( - (Integer) methodGetPID.invoke(notifications[i], - new Object[]{})); + for (int i = 0; i < n; i++) { + // getName() + k = (StringValue) StringValue.create( + methodGetName.invoke(notifications[i], + new Object[]{})); + // getPID() + v = (LongValue) LongValue.create( + (Integer) methodGetPID.invoke(notifications[i], + new Object[]{})); - arrayValue.put(k, v); - } + arrayValue.put(k, v); + } - return arrayValue; + return arrayValue; + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } + + /** + * Gets the backend's process ID + */ + public static int pg_get_pid(Env env, + @NotNull Postgres conn) { + try { + if (conn == null) { + return -1; + } - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } + // @todo create a random string + String randomLabel = "caucho_pg_get_pid_random_label"; - /** - * Gets the backend's process ID - */ - public static int pg_get_pid(Env env, - @NotNull Postgres conn) { - try { - if (conn == null) { + pg_query(env, conn, "LISTEN " + randomLabel); + pg_query(env, conn, "NOTIFY " + randomLabel); + + ArrayValue arrayValue = pg_get_notify(env, conn, -1); + + LongValue pid = (LongValue) arrayValue.get(StringValue.create(randomLabel)); + + return pid.toInt(); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return -1; - } - - // @todo create a random string - String randomLabel = "caucho_pg_get_pid_random_label"; - - pg_query(env, conn, "LISTEN " + randomLabel); - pg_query(env, conn, "NOTIFY " + randomLabel); - - ArrayValue arrayValue = pg_get_notify(env, conn, -1); - - LongValue pid = (LongValue) arrayValue.get(StringValue.create(randomLabel)); - - return pid.toInt(); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return -1; - } - } - - /** - * Get asynchronous query result - */ - @ReturnNullAsFalse - public static PostgresResult pg_get_result(Env env, - @Optional Postgres conn) { - // Three different scenarios for pg_get_result: - // - // 1. pg_send_prepare/pg_send_execute - php/431m - // - // pg_send_prepare( - // $conn, "my_query", 'SELECT * FROM test WHERE data = $1'); - // $res1 = pg_get_result($conn); - // - // pg_send_execute($conn, "my_query", array("Joe's Widgets")); - // $res2 = pg_get_result($conn); - // - // pg_send_execute($conn, "my_query", array("Clothes Clothes Clothes")); - // $res3 = pg_get_result($conn); - // - // 2. Multiquery with pg_send_query - php/430y - // - // pg_send_query( - // $conn, "select * from test; select count(*) from test;"); - // - // // select * from test - // $res = pg_get_result($conn); - // $rows = pg_num_rows($res); - // - // // select count(*) from test - // $res = pg_get_result($conn); - // $rows = pg_num_rows($res); - // - // 3. Individual pg_send_query - php/431g - // - // $res = pg_send_query($conn, "select * from test;"); - // var_dump($res); - // $res = pg_get_result($conn); - // var_dump($res); - // - // $res = pg_send_query($conn, "select * from doesnotexist;"); - // var_dump($res); - // $res = pg_get_result($conn); - // var_dump($res); - - try { - if (conn == null) { - conn = getConnection(env); - } + } + } + + /** + * Get asynchronous query result + */ + @ReturnNullAsFalse + public static PostgresResult pg_get_result(Env env, + @Optional Postgres conn) { + // Three different scenarios for pg_get_result: + // + // 1. pg_send_prepare/pg_send_execute - php/431m + // + // pg_send_prepare( + // $conn, "my_query", 'SELECT * FROM test WHERE data = $1'); + // $res1 = pg_get_result($conn); + // + // pg_send_execute($conn, "my_query", array("Joe's Widgets")); + // $res2 = pg_get_result($conn); + // + // pg_send_execute($conn, "my_query", array("Clothes Clothes Clothes")); + // $res3 = pg_get_result($conn); + // + // 2. Multiquery with pg_send_query - php/430y + // + // pg_send_query( + // $conn, "select * from test; select count(*) from test;"); + // + // // select * from test + // $res = pg_get_result($conn); + // $rows = pg_num_rows($res); + // + // // select count(*) from test + // $res = pg_get_result($conn); + // $rows = pg_num_rows($res); + // + // 3. Individual pg_send_query - php/431g + // + // $res = pg_send_query($conn, "select * from test;"); + // var_dump($res); + // $res = pg_get_result($conn); + // var_dump($res); + // + // $res = pg_send_query($conn, "select * from doesnotexist;"); + // var_dump($res); + // $res = pg_get_result($conn); + // var_dump($res); + + try { + if (conn == null) { + conn = getConnection(env); + } - PostgresResult result = (PostgresResult) conn.getResultResource(); + PostgresResult result = (PostgresResult) conn.getResultResource(); - // 1. pg_send_prepare/pg_send_execute - if (conn.getAsynchronousStatement() != null) { + // 1. pg_send_prepare/pg_send_execute + if (conn.getAsynchronousStatement() != null) { + if (conn.getAsynchronousResult() != null) { + conn.setAsynchronousResult(null); + return result; + } + return null; + } + + // 2. pg_send_query if (conn.getAsynchronousResult() != null) { - conn.setAsynchronousResult(null); - return result; + + // Check for next result + // Ex: pg_send_query( + // $conn, "select * from test; select count(*) from test;"); + + Statement stmt = result.getJavaStatement(); + + if (stmt.getMoreResults()) { + result = (PostgresResult) conn.createResult(env, stmt, + stmt.getResultSet()); + } else { + // 3. Individual pg_send_query (clean up; no futher results) + conn.setResultResource(null); + } } + + conn.setAsynchronousResult(result); + + return result; + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } + } + } + + /** + * Returns the host name associated with the connection + */ + @ReturnNullAsFalse + public static String pg_host(Env env, + @Optional Postgres conn) { + try { + if (conn == null) { + conn = getConnection(env); + } - // 2. pg_send_query - if (conn.getAsynchronousResult() != null) { + return conn.getHost(); - // Check for next result - // Ex: pg_send_query( - // $conn, "select * from test; select count(*) from test;"); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } + + /** + * Insert array into table + */ + public static boolean pg_insert(Env env, + @NotNull Postgres conn, + String tableName, + ArrayValue assocArray, + @Optional("-1") int options) { + try { + if (conn == null) { + return false; + } - Statement stmt = result.getJavaStatement(); + if (options > 0) { + throw new UnimplementedException("pg_insert with options"); + } - if (stmt.getMoreResults()) { - result = (PostgresResult) conn.createResult(env, stmt, - stmt.getResultSet()); - } else { - // 3. Individual pg_send_query (clean up; no futher results) - conn.setResultResource(null); + StringBuilder names = new StringBuilder(); + StringBuilder values = new StringBuilder(); + + boolean isFirst = true; + + for (Map.Entry entry : assocArray.entrySet()) { + Value k = entry.getKey(); + Value v = entry.getValue(); + if (isFirst) { + isFirst = false; + } else { + values.append("','"); + names.append(","); + } + values.append(v.toString()); + names.append(k.toString()); } - } - conn.setAsynchronousResult(result); + StringBuilder query = new StringBuilder(); + query.append("INSERT INTO "); + query.append(tableName); + query.append("("); + query.append(names); + query.append(") VALUES('"); + query.append(values); + query.append("')"); - return result; + pg_query(env, conn, query.toString()); - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } + return true; - /** - * Returns the host name associated with the connection - */ - @ReturnNullAsFalse - public static String pg_host(Env env, - @Optional Postgres conn) { - try { - if (conn == null) { - conn = getConnection(env); - } - - return conn.getHost(); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Insert array into table - */ - public static boolean pg_insert(Env env, - @NotNull Postgres conn, - String tableName, - ArrayValue assocArray, - @Optional("-1") int options) { - try { - if (conn == null) { + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return false; - } + } + } + + /** + * Get the last error message string of a connection + */ + @ReturnNullAsFalse + public static StringValue pg_last_error(Env env, + @Optional Postgres conn) { + try { + + if (conn == null) { + conn = getConnection(env); + } - if (options > 0) { - throw new UnimplementedException("pg_insert with options"); - } + return conn.error(env); - StringBuilder names = new StringBuilder(); - StringBuilder values = new StringBuilder(); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } + + /** + * pg_last_error() alias. + */ + @ReturnNullAsFalse + public static StringValue pg_errormessage(Env env, + @Optional Postgres conn) { + return pg_last_error(env, conn); + } + + /** + * Returns the last notice message from PostgreSQL server + */ + @ReturnNullAsFalse + public static String pg_last_notice(Env env, + @NotNull Postgres conn) { + try { + if (conn == null) { + return null; + } - boolean isFirst = true; + SQLWarning warning = conn.getWarnings(); - for (Map.Entry entry : assocArray.entrySet()) { - Value k = entry.getKey(); - Value v = entry.getValue(); - if (isFirst) { - isFirst = false; + if (warning != null) { + return warning.toString(); } else { - values.append("','"); - names.append(","); - } - values.append(v.toString()); - names.append(k.toString()); - } - - StringBuilder query = new StringBuilder(); - query.append("INSERT INTO "); - query.append(tableName); - query.append("("); - query.append(names); - query.append(") VALUES('"); - query.append(values); - query.append("')"); - - pg_query(env, conn, query.toString()); - - return true; - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return false; - } - } - - /** - * Get the last error message string of a connection - */ - @ReturnNullAsFalse - public static StringValue pg_last_error(Env env, - @Optional Postgres conn) { - try { - - if (conn == null) { - conn = getConnection(env); - } - - return conn.error(env); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * pg_last_error() alias. - */ - @ReturnNullAsFalse - public static StringValue pg_errormessage(Env env, - @Optional Postgres conn) { - return pg_last_error(env, conn); - } - - /** - * Returns the last notice message from PostgreSQL server - */ - @ReturnNullAsFalse - public static String pg_last_notice(Env env, - @NotNull Postgres conn) { - try { - if (conn == null) { - return null; - } - - SQLWarning warning = conn.getWarnings(); + return null; + } - if (warning != null) { - return warning.toString(); - } else { + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Returns the last row's OID - * - * Note that: - * - OID is a unique id. It will not work if the table was - * created with "No oid". - * - MySql's "mysql_insert_id" receives the conection handler as argument but - * PostgreSQL's "pg_last_oid" uses the result handler. - */ - @ReturnNullAsFalse - public static String pg_last_oid(Env env, - PostgresResult result) { - try { - - Statement stmt = result.getJavaStatement(); - - Class cl = Class.forName("org.postgresql.jdbc2.AbstractJdbc2Statement"); - - Method method = cl.getDeclaredMethod("getLastOID", (Class) null); - - int oid = Integer.parseInt( - method.invoke(stmt, new Object[]{}).toString()); - - if (oid > 0) { - return "" + oid; - } - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - } - - return null; - } - - @ReturnNullAsFalse - public static String pg_getlastoid(Env env, - PostgresResult result) { - return pg_last_oid(env, result); - } - - /** - * Close a large object - */ - public static boolean pg_lo_close(Env env, - Object largeObject) { - try { - - Class cl = Class.forName("org.postgresql.largeobject.LargeObject"); - - Method method = cl.getDeclaredMethod("close", (Class) null); - - method.invoke(largeObject, new Object[]{}); - // largeObject.close(); - - return true; - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return false; - } - } - - /** - * pg_lo_close() alias. - */ - public static boolean pg_loclose(Env env, - Object largeObject) { - return pg_lo_close(env, largeObject); - } - - /** - * Create a large object - */ - @ReturnNullAsFalse - public static LongValue pg_lo_create(Env env, - @Optional Postgres conn) { - try { - - int oid = -1; - - if (conn == null) { - conn = getConnection(env); - } - - // LargeObjectManager lobManager; - Object lobManager; + } + } + + /** + * Returns the last row's OID + * + * Note that: + * - OID is a unique id. It will not work if the table was + * created with "No oid". + * - MySql's "mysql_insert_id" receives the conection handler as argument but + * PostgreSQL's "pg_last_oid" uses the result handler. + */ + @ReturnNullAsFalse + public static String pg_last_oid(Env env, + PostgresResult result) { + try { - // org.postgresql.PGConnection - Class cl = Class.forName("org.postgresql.PGConnection"); + Statement stmt = result.getJavaStatement(); - Method method = cl.getDeclaredMethod("getLargeObjectAPI", (Class) null); + Class cl = Class.forName("org.postgresql.jdbc2.AbstractJdbc2Statement"); - Connection pgconn = conn.getJavaConnection(); + Method method = cl.getDeclaredMethod("getLastOID", (Class) null); - // Large Objects may not be used in auto-commit mode. - pgconn.setAutoCommit(false); + int oid = Integer.parseInt( + method.invoke(stmt, new Object[]{}).toString()); - lobManager = method.invoke(pgconn, new Object[]{}); - // lobManager = ((org.postgresql.PGConnection)conn).getLargeObjectAPI(); + if (oid > 0) { + return "" + oid; + } - // org.postgresql.largeobject.LargeObjectManager - cl = Class.forName("org.postgresql.largeobject.LargeObjectManager"); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + } - method = cl.getDeclaredMethod("create", (Class) null); + return null; + } - Object oidObj = method.invoke(lobManager, new Object[]{}); + @ReturnNullAsFalse + public static String pg_getlastoid(Env env, + PostgresResult result) { + return pg_last_oid(env, result); + } - oid = Integer.parseInt(oidObj.toString()); + /** + * Close a large object + */ + public static boolean pg_lo_close(Env env, + Object largeObject) { + try { - // oid = lobManager.create(); + Class cl = Class.forName("org.postgresql.largeobject.LargeObject"); - return LongValue.create(oid); + Method method = cl.getDeclaredMethod("close", (Class) null); - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } + method.invoke(largeObject, new Object[]{}); + // largeObject.close(); - /** - * pg_lo_create() alias - */ - @ReturnNullAsFalse - public static LongValue pg_locreate(Env env, - @Optional Postgres conn) { - return pg_lo_create(env, conn); - } + return true; - /** - * Export a large object to a file - */ - public static boolean pg_lo_export(Env env, - @NotNull Postgres conn, - int oid, - Path path) { - try { - if (conn == null) { + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return false; - } - - //@todo conn should be optional + } + } + + /** + * pg_lo_close() alias. + */ + public static boolean pg_loclose(Env env, + Object largeObject) { + return pg_lo_close(env, largeObject); + } + + /** + * Create a large object + */ + @ReturnNullAsFalse + public static LongValue pg_lo_create(Env env, + @Optional Postgres conn) { + try { + + int oid = -1; + + if (conn == null) { + conn = getConnection(env); + } - // LargeObjectManager lobManager; - Object lobManager; + // LargeObjectManager lobManager; + Object lobManager; - //org.postgresql.largeobject.LargeObjectManager + // org.postgresql.PGConnection + Class cl = Class.forName("org.postgresql.PGConnection"); - Class cl = Class.forName("org.postgresql.PGConnection"); + Method method = cl.getDeclaredMethod("getLargeObjectAPI", (Class) null); - Method method = cl.getDeclaredMethod("getLargeObjectAPI", (Class) null); + Connection pgconn = conn.getJavaConnection(); - Connection pgconn = conn.getJavaConnection(); + // Large Objects may not be used in auto-commit mode. + pgconn.setAutoCommit(false); - lobManager = method.invoke(pgconn, new Object[]{}); - // lobManager = ((org.postgresql.PGConnection)conn).getLargeObjectAPI(); + lobManager = method.invoke(pgconn, new Object[]{}); + // lobManager = ((org.postgresql.PGConnection)conn).getLargeObjectAPI(); - cl = Class.forName("org.postgresql.largeobject.LargeObjectManager"); + // org.postgresql.largeobject.LargeObjectManager + cl = Class.forName("org.postgresql.largeobject.LargeObjectManager"); - method = cl.getDeclaredMethod("open", new Class[]{Integer.TYPE}); + method = cl.getDeclaredMethod("create", (Class) null); - Object lobj = method.invoke(lobManager, new Object[]{oid}); + Object oidObj = method.invoke(lobManager, new Object[]{}); - cl = Class.forName("org.postgresql.largeobject.LargeObject"); + oid = Integer.parseInt(oidObj.toString()); - method = cl.getDeclaredMethod("getInputStream", (Class) null); + // oid = lobManager.create(); - Object isObj = method.invoke(lobj, new Object[]{}); + return LongValue.create(oid); - InputStream is = (InputStream) isObj; + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } + + /** + * pg_lo_create() alias + */ + @ReturnNullAsFalse + public static LongValue pg_locreate(Env env, + @Optional Postgres conn) { + return pg_lo_create(env, conn); + } + + /** + * Export a large object to a file + */ + public static boolean pg_lo_export(Env env, + @NotNull Postgres conn, + int oid, + Path path) { + try { + if (conn == null) { + return false; + } - // Open the file - WriteStream os = path.openWrite(); + //@todo conn should be optional - // copy the data from the large object to the file - os.writeStream(is); + // LargeObjectManager lobManager; + Object lobManager; - os.close(); - is.close(); + //org.postgresql.largeobject.LargeObjectManager - // Close the large object - method = cl.getDeclaredMethod("close", (Class) null); + Class cl = Class.forName("org.postgresql.PGConnection"); - method.invoke(lobj, new Object[]{}); + Method method = cl.getDeclaredMethod("getLargeObjectAPI", (Class) null); - return true; + Connection pgconn = conn.getJavaConnection(); - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return false; - } - } + lobManager = method.invoke(pgconn, new Object[]{}); + // lobManager = ((org.postgresql.PGConnection)conn).getLargeObjectAPI(); - /** - * pg_lo_export() alias. - */ - public static boolean pg_loexport(Env env, - @NotNull Postgres conn, - int oid, - Path path) { - return pg_lo_export(env, conn, oid, path); - } + cl = Class.forName("org.postgresql.largeobject.LargeObjectManager"); - /** - * Import a large object from file - */ - @ReturnNullAsFalse - public static LongValue pg_lo_import(Env env, - @NotNull Postgres conn, - Path path) { - try { - if (conn == null) { - return null; - } + method = cl.getDeclaredMethod("open", new Class[]{Integer.TYPE}); - //@todo conn should be optional + Object lobj = method.invoke(lobManager, new Object[]{oid}); - LongValue value = pg_lo_create(env, conn); + cl = Class.forName("org.postgresql.largeobject.LargeObject"); - if (value != null) { + method = cl.getDeclaredMethod("getInputStream", (Class) null); - int oid = value.toInt(); - Object largeObject = pg_lo_open(env, conn, oid, "w"); + Object isObj = method.invoke(lobj, new Object[]{}); - String data = ""; + InputStream is = (InputStream) isObj; // Open the file - ReadStream is = path.openRead(); - - writeLobInternal(largeObject, is, Integer.MAX_VALUE); + WriteStream os = path.openWrite(); - pg_lo_close(env, largeObject); + // copy the data from the large object to the file + os.writeStream(is); + os.close(); is.close(); - return LongValue.create(oid); - } - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - } - - return null; - } - - /** - * pg_lo_import() alias. - */ - @ReturnNullAsFalse - public static LongValue pg_loimport(Env env, - @NotNull Postgres conn, - Path path) { - return pg_lo_import(env, conn, path); - } - - /** - * Open a large object - */ - @ReturnNullAsFalse - public static Object pg_lo_open(Env env, - @NotNull Postgres conn, - int oid, - String mode) { - try { - if (conn == null) { - return null; - } + // Close the large object + method = cl.getDeclaredMethod("close", (Class) null); + + method.invoke(lobj, new Object[]{}); - Object largeObject = null; + return true; - // LargeObjectManager lobManager; - Object lobManager; + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return false; + } + } + + /** + * pg_lo_export() alias. + */ + public static boolean pg_loexport(Env env, + @NotNull Postgres conn, + int oid, + Path path) { + return pg_lo_export(env, conn, oid, path); + } + + /** + * Import a large object from file + */ + @ReturnNullAsFalse + public static LongValue pg_lo_import(Env env, + @NotNull Postgres conn, + Path path) { + try { + if (conn == null) { + return null; + } - //org.postgresql.largeobject.LargeObjectManager + //@todo conn should be optional - Class cl = Class.forName("org.postgresql.PGConnection"); + LongValue value = pg_lo_create(env, conn); - Method method = cl.getDeclaredMethod("getLargeObjectAPI", (Class) null); + if (value != null) { - Connection pgconn = conn.getJavaConnection(); + int oid = value.toInt(); + Object largeObject = pg_lo_open(env, conn, oid, "w"); - lobManager = method.invoke(pgconn, new Object[]{}); + String data = ""; - cl = Class.forName("org.postgresql.largeobject.LargeObjectManager"); + // Open the file + ReadStream is = path.openRead(); - method = cl.getDeclaredMethod("open", - new Class[]{Integer.TYPE, Integer.TYPE}); + writeLobInternal(largeObject, is, Integer.MAX_VALUE); - boolean write = mode.indexOf("w") >= 0; - boolean read = mode.indexOf("r") >= 0; + pg_lo_close(env, largeObject); - int modeREAD = cl.getDeclaredField("READ").getInt(null); - int modeREADWRITE = cl.getDeclaredField("READWRITE").getInt(null); - int modeWRITE = cl.getDeclaredField("WRITE").getInt(null); + is.close(); - int intMode = modeREAD; + return LongValue.create(oid); + } - if (read) { - if (write) { - intMode = modeREADWRITE; + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + } + + return null; + } + + /** + * pg_lo_import() alias. + */ + @ReturnNullAsFalse + public static LongValue pg_loimport(Env env, + @NotNull Postgres conn, + Path path) { + return pg_lo_import(env, conn, path); + } + + /** + * Open a large object + */ + @ReturnNullAsFalse + public static Object pg_lo_open(Env env, + @NotNull Postgres conn, + int oid, + String mode) { + try { + if (conn == null) { + return null; } - } else if (write) { - intMode = modeWRITE; - } - largeObject = method.invoke(lobManager, new Object[]{oid, intMode}); + Object largeObject = null; - return largeObject; + // LargeObjectManager lobManager; + Object lobManager; - } catch (Exception ex) { - env.warning(L.l("Unable to open PostgreSQL large object")); - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } + //org.postgresql.largeobject.LargeObjectManager - /** - * pg_lo_open() alias. - */ - @ReturnNullAsFalse - public static Object pg_loopen(Env env, - @NotNull Postgres conn, - int oid, - String mode) { - return pg_lo_open(env, conn, oid, mode); - } + Class cl = Class.forName("org.postgresql.PGConnection"); - /** - * Reads an entire large object and send straight to browser - */ - @ReturnNullAsFalse - public static LongValue pg_lo_read_all(Env env, - Object largeObject) { - try { - StringValue contents = pg_lo_read(env, largeObject, -1); + Method method = cl.getDeclaredMethod("getLargeObjectAPI", (Class) null); - if (contents != null) { - env.getOut().print(contents); - } + Connection pgconn = conn.getJavaConnection(); - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - } + lobManager = method.invoke(pgconn, new Object[]{}); - return null; - } + cl = Class.forName("org.postgresql.largeobject.LargeObjectManager"); - /** - * pg_lo_read_all() alias. - */ - @ReturnNullAsFalse - public static LongValue pg_loreadall(Env env, - Object largeObject) { - return pg_lo_read_all(env, largeObject); - } + method = cl.getDeclaredMethod("open", + new Class[]{Integer.TYPE, Integer.TYPE}); - /** - * Read a large object - */ - @ReturnNullAsFalse - public static StringValue pg_lo_read(Env env, - Object largeObject, - @Optional("-1") int len) { - try { + boolean write = mode.indexOf("w") >= 0; + boolean read = mode.indexOf("r") >= 0; - if (len < 0) { - len = Integer.MAX_VALUE; - } + int modeREAD = cl.getDeclaredField("READ").getInt(null); + int modeREADWRITE = cl.getDeclaredField("READWRITE").getInt(null); + int modeWRITE = cl.getDeclaredField("WRITE").getInt(null); - Class cl = Class.forName("org.postgresql.largeobject.LargeObject"); + int intMode = modeREAD; - Method method = cl.getDeclaredMethod("getInputStream", (Class) null); + if (read) { + if (write) { + intMode = modeREADWRITE; + } + } else if (write) { + intMode = modeWRITE; + } - InputStream is = (InputStream) method.invoke(largeObject, new Object[]{}); + largeObject = method.invoke(lobManager, new Object[]{oid, intMode}); - try { - StringValue bb = env.createBinaryBuilder(); + return largeObject; - bb.appendReadAll(is, len); + } catch (Exception ex) { + env.warning(L.l("Unable to open PostgreSQL large object")); + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } + + /** + * pg_lo_open() alias. + */ + @ReturnNullAsFalse + public static Object pg_loopen(Env env, + @NotNull Postgres conn, + int oid, + String mode) { + return pg_lo_open(env, conn, oid, mode); + } + + /** + * Reads an entire large object and send straight to browser + */ + @ReturnNullAsFalse + public static LongValue pg_lo_read_all(Env env, + Object largeObject) { + try { + StringValue contents = pg_lo_read(env, largeObject, -1); + + if (contents != null) { + env.getOut().print(contents); + } - return bb; - } finally { - is.close(); - } - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * pg_lo_read() alias. - */ - @ReturnNullAsFalse - public static StringValue pg_loread(Env env, - Object largeObject, - @Optional("-1") int len) { - return pg_lo_read(env, largeObject, len); - } - - /** - * Seeks position within a large object - */ - public static boolean pg_lo_seek(Env env, - Object largeObject, - int offset, - @Optional int whence) { - try { - - Class cl = Class.forName("org.postgresql.largeobject.LargeObject"); - - int seekSET = cl.getDeclaredField("SEEK_SET").getInt(null); - int seekEND = cl.getDeclaredField("SEEK_END").getInt(null); - int seekCUR = cl.getDeclaredField("SEEK_CUR").getInt(null); - - switch (whence) { - case PGSQL_SEEK_SET: - whence = seekSET; - break; - case PGSQL_SEEK_END: - whence = seekEND; - break; - default: - whence = seekCUR; - } - - Method method = cl.getDeclaredMethod( - "seek", new Class[]{Integer.TYPE, Integer.TYPE}); - - method.invoke(largeObject, new Object[]{offset, whence}); - - return true; - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return false; - } - } - - /** - * Returns current seek position a of large object - */ - public static int pg_lo_tell(Env env, - Object largeObject) { - try { - - Class cl = Class.forName("org.postgresql.largeobject.LargeObject"); - - Method method = cl.getDeclaredMethod("tell", (Class) null); - - Object obj = method.invoke(largeObject, new Object[]{}); - - return Integer.parseInt(obj.toString()); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return -1; - } - } - - /** - * Delete a large object - */ - public static boolean pg_lo_unlink(Env env, - @NotNull Postgres conn, - int oid) { - try { - if (conn == null) { + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + } + + return null; + } + + /** + * pg_lo_read_all() alias. + */ + @ReturnNullAsFalse + public static LongValue pg_loreadall(Env env, + Object largeObject) { + return pg_lo_read_all(env, largeObject); + } + + /** + * Read a large object + */ + @ReturnNullAsFalse + public static StringValue pg_lo_read(Env env, + Object largeObject, + @Optional("-1") int len) { + try { + + if (len < 0) { + len = Integer.MAX_VALUE; + } + + Class cl = Class.forName("org.postgresql.largeobject.LargeObject"); + + Method method = cl.getDeclaredMethod("getInputStream", (Class) null); + + InputStream is = (InputStream) method.invoke(largeObject, new Object[]{}); + + try { + StringValue bb = env.createBinaryBuilder(); + + bb.appendReadAll(is, len); + + return bb; + } finally { + is.close(); + } + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } + + /** + * pg_lo_read() alias. + */ + @ReturnNullAsFalse + public static StringValue pg_loread(Env env, + Object largeObject, + @Optional("-1") int len) { + return pg_lo_read(env, largeObject, len); + } + + /** + * Seeks position within a large object + */ + public static boolean pg_lo_seek(Env env, + Object largeObject, + int offset, + @Optional int whence) { + try { + + Class cl = Class.forName("org.postgresql.largeobject.LargeObject"); + + int seekSET = cl.getDeclaredField("SEEK_SET").getInt(null); + int seekEND = cl.getDeclaredField("SEEK_END").getInt(null); + int seekCUR = cl.getDeclaredField("SEEK_CUR").getInt(null); + + switch (whence) { + case PGSQL_SEEK_SET: + whence = seekSET; + break; + case PGSQL_SEEK_END: + whence = seekEND; + break; + default: + whence = seekCUR; + } + + Method method = cl.getDeclaredMethod( + "seek", new Class[]{Integer.TYPE, Integer.TYPE}); + + method.invoke(largeObject, new Object[]{offset, whence}); + + return true; + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return false; - } + } + } - // LargeObjectManager lobManager; - Object lobManager; + /** + * Returns current seek position a of large object + */ + public static int pg_lo_tell(Env env, + Object largeObject) { + try { - //org.postgresql.largeobject.LargeObjectManager + Class cl = Class.forName("org.postgresql.largeobject.LargeObject"); - Class cl = Class.forName("org.postgresql.PGConnection"); + Method method = cl.getDeclaredMethod("tell", (Class) null); - Method method = cl.getDeclaredMethod("getLargeObjectAPI", (Class) null); + Object obj = method.invoke(largeObject, new Object[]{}); - Connection pgconn = conn.getJavaConnection(); + return Integer.parseInt(obj.toString()); - lobManager = method.invoke(pgconn, new Object[]{}); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return -1; + } + } + + /** + * Delete a large object + */ + public static boolean pg_lo_unlink(Env env, + @NotNull Postgres conn, + int oid) { + try { + if (conn == null) { + return false; + } - cl = Class.forName("org.postgresql.largeobject.LargeObjectManager"); + // LargeObjectManager lobManager; + Object lobManager; - method = cl.getDeclaredMethod("unlink", new Class[]{Integer.TYPE}); + //org.postgresql.largeobject.LargeObjectManager - method.invoke(lobManager, new Object[]{oid}); + Class cl = Class.forName("org.postgresql.PGConnection"); - return true; + Method method = cl.getDeclaredMethod("getLargeObjectAPI", (Class) null); - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return false; - } - } + Connection pgconn = conn.getJavaConnection(); - /** - * pg_lo_unlink() alias. - */ - public static boolean pg_lounlink(Env env, - @NotNull Postgres conn, - int oid) { - return pg_lo_unlink(env, conn, oid); - } + lobManager = method.invoke(pgconn, new Object[]{}); - /** - * Write to a large object - */ - @ReturnNullAsFalse - public static LongValue pg_lo_write(Env env, - @NotNull Object largeObject, - String data, - @Optional int len) { - try { - if (largeObject == null) { - return null; - } - - if (len <= 0) { - len = data.length(); - } - - int written = len; - - Class cl = Class.forName("org.postgresql.largeobject.LargeObject"); - - Method method = cl.getDeclaredMethod("write", - new Class[]{byte[].class, - Integer.TYPE, - Integer.TYPE}); - - method.invoke(largeObject, new Object[]{data.getBytes(), 0, len}); - - return LongValue.create(written); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * pg_lo_write() alias. - */ - @ReturnNullAsFalse - public static LongValue pg_lowrite(Env env, - @NotNull Object largeObject, - String data, - @Optional int len) { - return pg_lo_write(env, largeObject, data, len); - } - - /** - * Get meta data for table - */ - @ReturnNullAsFalse - public static ArrayValue pg_meta_data(Env env, - @NotNull Postgres conn, - String tableName) { - env.stub("pg_meta_data"); - - return null; - } - - /** - * Returns the number of fields in a result - */ - public static int pg_num_fields(Env env, - @NotNull PostgresResult result) { - try { - return result.getFieldCount(); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return -1; - } - } - - /** - * pg_num_fields() alias. - */ - public static int pg_numfields(Env env, - @NotNull PostgresResult result) { - return pg_num_fields(env, result); - } - - /** - * Returns the number of rows in a result - */ - public static LongValue pg_num_rows(Env env, - @NotNull PostgresResult result) { - int numRows = -1; - - try { - if (result == null) { - return LongValue.create(-1); - } - - if ((result != null) && (result.getResultSet() != null)) { - numRows = result.getNumRows(); - } - - if (numRows < 0) { - env.warning(L.l( - "supplied argument is not a valid PostgreSQL result resource")); - } - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - } - - return LongValue.create(numRows); - } - - /** - * pg_num_rows() alias. - */ - public static LongValue pg_numrows(Env env, - @NotNull PostgresResult result) { - return pg_num_rows(env, result); - } - - /** - * Get the options associated with the connection - */ - public static String pg_options(Env env, - @Optional Postgres conn) { - throw new UnimplementedException("pg_options"); - } - - /** - * Looks up a current parameter setting of the server - */ - public static Value pg_parameter_status(Env env, - @NotNull Postgres conn, - @NotNull StringValue paramName) { - try { - if (conn == null || paramName == null) { - return BooleanValue.FALSE; - } + cl = Class.forName("org.postgresql.largeobject.LargeObjectManager"); - PostgresResult result = pg_query(env, conn, "SHOW " + paramName); + method = cl.getDeclaredMethod("unlink", new Class[]{Integer.TYPE}); - Value value = pg_fetch_result( - env, result, LongValue.ZERO, LongValue.ZERO); + method.invoke(lobManager, new Object[]{oid}); - if (value == null || value.isNull()) { - return BooleanValue.FALSE; - } - - if (paramName.toString().equals("server_encoding")) { - if (value.equals(env.createString("UNICODE"))) { - value = env.createString("UTF8"); - } - } - - return value; - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return BooleanValue.FALSE; - } - } - - /** - * Looks up a current parameter setting of the server - */ - public static Value pg_parameter_status(Env env, - @NotNull StringValue paramName) { - Postgres conn = getConnection(env); - - return pg_parameter_status(env, conn, paramName); - } - - /** - * Open a persistent PostgreSQL connection - */ - @ReturnNullAsFalse - public static Postgres pg_pconnect(Env env, - String connectionString, - @Optional int connectType) { - return pg_connect(env, connectionString, connectType); - } - - /** - * Ping database connection - */ - public static boolean pg_ping(Env env, - @Optional Postgres conn) { - try { - - if (conn == null) { - conn = getConnection(env); - } + return true; + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return false; + } + } + + /** + * pg_lo_unlink() alias. + */ + public static boolean pg_lounlink(Env env, + @NotNull Postgres conn, + int oid) { + return pg_lo_unlink(env, conn, oid); + } + + /** + * Write to a large object + */ + @ReturnNullAsFalse + public static LongValue pg_lo_write(Env env, + @NotNull Object largeObject, + String data, + @Optional int len) { + try { + if (largeObject == null) { + return null; + } - return pg_query(env, conn, "SELECT 1") != null; + if (len <= 0) { + len = data.length(); + } - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return false; - } - } + int written = len; - /** - * Return the port number associated with the connection - */ - @ReturnNullAsFalse - public static StringValue pg_port(Env env, - @Optional Postgres conn) { - try { + Class cl = Class.forName("org.postgresql.largeobject.LargeObject"); - if (conn == null) { - conn = getConnection(env); - } - - // In PHP, a real pg_port test case returns String - - return (StringValue) StringValue.create(conn.getPort()); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Submits a request to create a prepared statement with the given parameters, - * and waits for completion - */ - @ReturnNullAsFalse - public static PostgresStatement pg_prepare(Env env, - @NotNull Postgres conn, - String stmtName, - String query) { - try { - if (conn == null) { + Method method = cl.getDeclaredMethod("write", + new Class[]{byte[].class, + Integer.TYPE, + Integer.TYPE}); + + method.invoke(largeObject, new Object[]{data.getBytes(), 0, len}); + + return LongValue.create(written); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } - - PostgresStatement pstmt = conn.prepare(env, env.createString(query)); - conn.putStatement(stmtName, pstmt); - - return pstmt; - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Send a NULL-terminated string to PostgreSQL backend - */ - public static boolean pg_put_line(Env env, - @NotNull Postgres conn, - String data) { - try { - if (conn == null) { - return false; - } + } + } + + /** + * pg_lo_write() alias. + */ + @ReturnNullAsFalse + public static LongValue pg_lowrite(Env env, + @NotNull Object largeObject, + String data, + @Optional int len) { + return pg_lo_write(env, largeObject, data, len); + } + + /** + * Get meta data for table + */ + @ReturnNullAsFalse + public static ArrayValue pg_meta_data(Env env, + @NotNull Postgres conn, + String tableName) { + env.stub("pg_meta_data"); + + return null; + } + + /** + * Returns the number of fields in a result + */ + public static int pg_num_fields(Env env, + @NotNull PostgresResult result) { + try { + return result.getFieldCount(); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return -1; + } + } + + /** + * pg_num_fields() alias. + */ + public static int pg_numfields(Env env, + @NotNull PostgresResult result) { + return pg_num_fields(env, result); + } + + /** + * Returns the number of rows in a result + */ + public static LongValue pg_num_rows(Env env, + @NotNull PostgresResult result) { + int numRows = -1; + + try { + if (result == null) { + return LongValue.create(-1); + } + + if ((result != null) && (result.getResultSet() != null)) { + numRows = result.getNumRows(); + } + + if (numRows < 0) { + env.warning(L.l( + "supplied argument is not a valid PostgreSQL result resource")); + } + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + } + + return LongValue.create(numRows); + } + + /** + * pg_num_rows() alias. + */ + public static LongValue pg_numrows(Env env, + @NotNull PostgresResult result) { + return pg_num_rows(env, result); + } + + /** + * Get the options associated with the connection + */ + public static String pg_options(Env env, + @Optional Postgres conn) { + throw new UnimplementedException("pg_options"); + } + + /** + * Looks up a current parameter setting of the server + */ + public static Value pg_parameter_status(Env env, + @NotNull Postgres conn, + @NotNull StringValue paramName) { + try { + if (conn == null || paramName == null) { + return BooleanValue.FALSE; + } + + PostgresResult result = pg_query(env, conn, "SHOW " + paramName); + + Value value = pg_fetch_result( + env, result, LongValue.ZERO, LongValue.ZERO); + + if (value == null || value.isNull()) { + return BooleanValue.FALSE; + } - Class cl = Class.forName("org.postgresql.core.PGStream"); + if (paramName.toString().equals("server_encoding")) { + if (value.equals(env.createString("UNICODE"))) { + value = env.createString("UTF8"); + } + } - Constructor constructor = cl.getDeclaredConstructor(new Class[]{ - String.class, Integer.TYPE}); + return value; - Object object = constructor.newInstance( - new Object[]{conn.getHost(), conn.getPort()}); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return BooleanValue.FALSE; + } + } + + /** + * Looks up a current parameter setting of the server + */ + public static Value pg_parameter_status(Env env, + @NotNull StringValue paramName) { + Postgres conn = getConnection(env); + + return pg_parameter_status(env, conn, paramName); + } + + /** + * Open a persistent PostgreSQL connection + */ + @ReturnNullAsFalse + public static Postgres pg_pconnect(Env env, + String connectionString, + @Optional int connectType) { + return pg_connect(env, connectionString, connectType); + } + + /** + * Ping database connection + */ + public static boolean pg_ping(Env env, + @Optional Postgres conn) { + try { + + if (conn == null) { + conn = getConnection(env); + } - byte[] dataArray = data.getBytes(); + return pg_query(env, conn, "SELECT 1") != null; - Method method = cl.getDeclaredMethod("Send", new Class[]{byte[].class}); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return false; + } + } + + /** + * Return the port number associated with the connection + */ + @ReturnNullAsFalse + public static StringValue pg_port(Env env, + @Optional Postgres conn) { + try { + + if (conn == null) { + conn = getConnection(env); + } - method.invoke(object, new Object[]{dataArray}); + // In PHP, a real pg_port test case returns String - return true; + return (StringValue) StringValue.create(conn.getPort()); - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return false; - } + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } + + /** + * Submits a request to create a prepared statement with the given parameters, + * and waits for completion + */ + @ReturnNullAsFalse + public static PostgresStatement pg_prepare(Env env, + @NotNull Postgres conn, + String stmtName, + String query) { + try { + if (conn == null) { + return null; + } - } + PostgresStatement pstmt = conn.prepare(env, env.createString(query)); + conn.putStatement(stmtName, pstmt); - /** - * Submits a command to the server and waits for the result, - * with the ability to pass parameters separately from the SQL command text - */ - @ReturnNullAsFalse - public static PostgresResult pg_query_params(Env env, - @NotNull Postgres conn, - String query, - ArrayValue params) { - try { - if (conn == null) { + return pstmt; + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } - - if (pg_send_query_params(env, conn, query, params)) { - return (PostgresResult) conn.getResultResource(); - } - - return null; - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Execute a query - */ - @ReturnNullAsFalse - public static PostgresResult pg_query(Env env, @NotNull String query) { - if (query == null) { - return null; - } - - return pg_query_impl(env, getConnection(env), query, true); - } - - /** - * Execute a query - */ - @ReturnNullAsFalse - public static PostgresResult pg_query(Env env, - @NotNull Postgres conn, - @NotNull String query) { - if (conn == null || query == null) { - return null; - } - - return pg_query_impl(env, conn, query, true); - } - - /** - * pg_query() alias - */ - @ReturnNullAsFalse - public static PostgresResult pg_exec(Env env, - @NotNull Postgres conn, - String query) { - if (conn == null) { - return null; - } - - return pg_query(env, conn, query); - } - - /** - * Execute a query - */ - private static PostgresResult pg_query_impl(Env env, - Postgres conn, - String query, - boolean reportError) { - try { - // TODO: the PHP api allows conn to be optional but we - // totally disallow this case. - - if (conn == null) { - conn = getConnection(env); - } + } + } + + /** + * Send a NULL-terminated string to PostgreSQL backend + */ + public static boolean pg_put_line(Env env, + @NotNull Postgres conn, + String data) { + try { + if (conn == null) { + return false; + } + + Class cl = Class.forName("org.postgresql.core.PGStream"); + + Constructor constructor = cl.getDeclaredConstructor(new Class[]{ + String.class, Integer.TYPE}); + + Object object = constructor.newInstance( + new Object[]{conn.getHost(), conn.getPort()}); + + byte[] dataArray = data.getBytes(); - PostgresResult result = conn.query(env, query); + Method method = cl.getDeclaredMethod("Send", new Class[]{byte[].class}); - StringValue error = conn.error(env); + method.invoke(object, new Object[]{dataArray}); - if (error.length() != 0) { - if (reportError) { - env.warning(L.l("Query failed: {0}", error)); + return true; + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return false; + } + + } + + /** + * Submits a command to the server and waits for the result, + * with the ability to pass parameters separately from the SQL command text + */ + @ReturnNullAsFalse + public static PostgresResult pg_query_params(Env env, + @NotNull Postgres conn, + String query, + ArrayValue params) { + try { + if (conn == null) { + return null; + } + + if (pg_send_query_params(env, conn, query, params)) { + return (PostgresResult) conn.getResultResource(); } return null; - } - return result; + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } + + /** + * Execute a query + */ + @ReturnNullAsFalse + public static PostgresResult pg_query(Env env, @NotNull String query) { + if (query == null) { + return null; + } + + return pg_query_impl(env, getConnection(env), query, true); + } + + /** + * Execute a query + */ + @ReturnNullAsFalse + public static PostgresResult pg_query(Env env, + @NotNull Postgres conn, + @NotNull String query) { + if (conn == null || query == null) { + return null; + } + + return pg_query_impl(env, conn, query, true); + } + + /** + * pg_query() alias + */ + @ReturnNullAsFalse + public static PostgresResult pg_exec(Env env, + @NotNull Postgres conn, + String query) { + if (conn == null) { + return null; + } + + return pg_query(env, conn, query); + } + + /** + * Execute a query + */ + private static PostgresResult pg_query_impl(Env env, + Postgres conn, + String query, + boolean reportError) { + try { + // TODO: the PHP api allows conn to be optional but we + // totally disallow this case. + + if (conn == null) { + conn = getConnection(env); + } - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - } + PostgresResult result = conn.query(env, query); - return null; - } + StringValue error = conn.error(env); - /** - * Returns an individual field of an error report - */ - public static Value pg_result_error_field(Env env, - @NotNull PostgresResult result, - int fieldCode) { - try { - if (result == null) { - return BooleanValue.FALSE; - } - - Object errorField = null; - - // Get the postgres specific server error message - // org.postgresql.util.ServerErrorMessage - Object serverError = ((Postgres) result.getConnection()).getServerErrorMessage(); - - if (serverError != null) { - Class cl = Class.forName("org.postgresql.util.ServerErrorMessage"); - - String methodName; - - switch (fieldCode) { - case PGSQL_DIAG_SEVERITY: - methodName = "getSeverity"; - break; - case PGSQL_DIAG_SQLSTATE: - methodName = "getSQLState"; - break; - case PGSQL_DIAG_MESSAGE_PRIMARY: - methodName = "getMessage"; - break; - case PGSQL_DIAG_MESSAGE_DETAIL: - methodName = "getDetail"; - break; - case PGSQL_DIAG_MESSAGE_HINT: - methodName = "getHint"; - break; - case PGSQL_DIAG_STATEMENT_POSITION: - methodName = "getPosition"; - break; - case PGSQL_DIAG_INTERNAL_POSITION: - methodName = "getInternalPosition"; - break; - case PGSQL_DIAG_INTERNAL_QUERY: - methodName = "getInternalQuery"; - break; - case PGSQL_DIAG_CONTEXT: - methodName = "getWhere"; - break; - case PGSQL_DIAG_SOURCE_FILE: - methodName = "getFile"; - break; - case PGSQL_DIAG_SOURCE_LINE: - methodName = "getLine"; - break; - case PGSQL_DIAG_SOURCE_FUNCTION: - methodName = "getRoutine"; - break; - default: - return null; - } - - Method method = cl.getDeclaredMethod(methodName, (Class) null); - errorField = method.invoke(serverError, new Object[]{}); - } - - if (errorField == null) { - /* XXX: php/431g - if (fieldCode == PGSQL_DIAG_INTERNAL_QUERY) - return BooleanValue.FALSE; - */ + if (error.length() != 0) { + if (reportError) { + env.warning(L.l("Query failed: {0}", error)); + } - return NullValue.NULL; - } + return null; + } + + return result; + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + } + + return null; + } + + /** + * Returns an individual field of an error report + */ + public static Value pg_result_error_field(Env env, + @NotNull PostgresResult result, + int fieldCode) { + try { + if (result == null) { + return BooleanValue.FALSE; + } - if (fieldCode == PGSQL_DIAG_STATEMENT_POSITION) { + Object errorField = null; + + // Get the postgres specific server error message + // org.postgresql.util.ServerErrorMessage + Object serverError = ((Postgres) result.getConnection()).getServerErrorMessage(); + + if (serverError != null) { + Class cl = Class.forName("org.postgresql.util.ServerErrorMessage"); + + String methodName; + + switch (fieldCode) { + case PGSQL_DIAG_SEVERITY: + methodName = "getSeverity"; + break; + case PGSQL_DIAG_SQLSTATE: + methodName = "getSQLState"; + break; + case PGSQL_DIAG_MESSAGE_PRIMARY: + methodName = "getMessage"; + break; + case PGSQL_DIAG_MESSAGE_DETAIL: + methodName = "getDetail"; + break; + case PGSQL_DIAG_MESSAGE_HINT: + methodName = "getHint"; + break; + case PGSQL_DIAG_STATEMENT_POSITION: + methodName = "getPosition"; + break; + case PGSQL_DIAG_INTERNAL_POSITION: + methodName = "getInternalPosition"; + break; + case PGSQL_DIAG_INTERNAL_QUERY: + methodName = "getInternalQuery"; + break; + case PGSQL_DIAG_CONTEXT: + methodName = "getWhere"; + break; + case PGSQL_DIAG_SOURCE_FILE: + methodName = "getFile"; + break; + case PGSQL_DIAG_SOURCE_LINE: + methodName = "getLine"; + break; + case PGSQL_DIAG_SOURCE_FUNCTION: + methodName = "getRoutine"; + break; + default: + return null; + } + + Method method = cl.getDeclaredMethod(methodName, (Class) null); + errorField = method.invoke(serverError, new Object[]{}); + } - Integer position = (Integer) errorField; + if (errorField == null) { + /* XXX: php/431g + if (fieldCode == PGSQL_DIAG_INTERNAL_QUERY) + return BooleanValue.FALSE; + */ - if (position.intValue() == 0) { - return NullValue.NULL; + return NullValue.NULL; } - } - if (fieldCode == PGSQL_DIAG_INTERNAL_POSITION) { + if (fieldCode == PGSQL_DIAG_STATEMENT_POSITION) { - Integer position = (Integer) errorField; + Integer position = (Integer) errorField; - if (position.intValue() == 0) // TODO: php/431g return BooleanValue.FALSE; - { - return NullValue.NULL; + if (position.intValue() == 0) { + return NullValue.NULL; + } } - } - return StringValue.create(errorField.toString()); + if (fieldCode == PGSQL_DIAG_INTERNAL_POSITION) { + + Integer position = (Integer) errorField; + + if (position.intValue() == 0) // TODO: php/431g return BooleanValue.FALSE; + { + return NullValue.NULL; + } + } - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return NullValue.NULL; - } - } + return StringValue.create(errorField.toString()); - /** - * Get error message associated with result - */ - @ReturnNullAsFalse - public static String pg_result_error(Env env, - @Optional PostgresResult result) { - try { - if (result != null) { - return result.getConnection().getErrorMessage(); - } else { + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return NullValue.NULL; + } + } + + /** + * Get error message associated with result + */ + @ReturnNullAsFalse + public static String pg_result_error(Env env, + @Optional PostgresResult result) { + try { + if (result != null) { + return result.getConnection().getErrorMessage(); + } else { + return null; + } + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } - - /** - * Set internal row offset in result resource - */ - public static boolean pg_result_seek(Env env, - @NotNull PostgresResult result, - int offset) { - try { - if (result == null) { + } + } + + /** + * Set internal row offset in result resource + */ + public static boolean pg_result_seek(Env env, + @NotNull PostgresResult result, + int offset) { + try { + if (result == null) { + return false; + } + + return result.seek(env, offset); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return false; - } - - return result.seek(env, offset); - - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return false; - } - } - - /** - * Get status of query result - */ - public static int pg_result_status(Env env, - @NotNull PostgresResult result, - @Optional("PGSQL_STATUS_LONG") int type) { - try { - if (result == null) { - return -1; - } + } + } + + /** + * Get status of query result + */ + public static int pg_result_status(Env env, + @NotNull PostgresResult result, + @Optional("PGSQL_STATUS_LONG") int type) { + try { + if (result == null) { + return -1; + } - if (type == PGSQL_STATUS_STRING) { - throw new UnimplementedException( - "pg_result_status with PGSQL_STATUS_STRING"); - } + if (type == PGSQL_STATUS_STRING) { + throw new UnimplementedException( + "pg_result_status with PGSQL_STATUS_STRING"); + } - if (result != null) { - Statement stmt = result.getStatement(); - if (stmt.getUpdateCount() >= 0) { - return PGSQL_COMMAND_OK; + if (result != null) { + Statement stmt = result.getStatement(); + if (stmt.getUpdateCount() >= 0) { + return PGSQL_COMMAND_OK; + } + + ResultSet rs = result.getResultSet(); + if (rs == null) { + return PGSQL_EMPTY_QUERY; + } + + return PGSQL_TUPLES_OK; + } + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + } + + return -1; + } + + /** + * Select records + */ + @ReturnNullAsFalse + public static ArrayValue pg_select(Env env, + @NotNull Postgres conn, + String tableName, + ArrayValue assocArray, + @Optional("-1") int options) { + try { + if (conn == null) { + return null; } - ResultSet rs = result.getResultSet(); - if (rs == null) { - return PGSQL_EMPTY_QUERY; + StringValue whereClause = env.createUnicodeBuilder(); + + boolean isFirst = true; + + for (Map.Entry entry : assocArray.entrySet()) { + Value k = entry.getKey(); + Value v = entry.getValue(); + if (isFirst) { + isFirst = false; + } else { + whereClause.append(" AND "); + } + whereClause.append(k.toString()).append("='").append(v.toString()).append("'"); + // String pi = conn.realEscapeString(p).toString(); + // pi = pi.replaceAll("\\\\", "\\\\\\\\"); } - return PGSQL_TUPLES_OK; - } + StringValue query = env.createUnicodeBuilder(); + query.append("SELECT * FROM ").append(tableName).append(" WHERE ").append(whereClause); - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - } + PostgresResult result = pg_query(env, conn, query.toString()); - return -1; - } + return pg_fetch_all(env, result); - /** - * Select records - */ - @ReturnNullAsFalse - public static ArrayValue pg_select(Env env, - @NotNull Postgres conn, - String tableName, - ArrayValue assocArray, - @Optional("-1") int options) { - try { - if (conn == null) { + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); return null; - } + } + } - StringValue whereClause = env.createUnicodeBuilder(); + /** + * Sends a request to execute a prepared statement with given parameters, + * without waiting for the result(s) + */ + public static boolean pg_send_execute(Env env, + @NotNull Postgres conn, + String stmtName, + ArrayValue params) { + try { - boolean isFirst = true; + // Note: for now, this is essentially the same as pg_execute. - for (Map.Entry entry : assocArray.entrySet()) { - Value k = entry.getKey(); - Value v = entry.getValue(); - if (isFirst) { - isFirst = false; - } else { - whereClause.append(" AND "); + PostgresResult result = pg_execute(env, conn, stmtName, params); + + conn.setAsynchronousResult(result); + + if (result != null) { + return true; } - whereClause.append(k.toString()).append("='").append(v.toString()).append("'"); - // String pi = conn.realEscapeString(p).toString(); - // pi = pi.replaceAll("\\\\", "\\\\\\\\"); - } - StringValue query = env.createUnicodeBuilder(); - query.append("SELECT * FROM ").append(tableName).append(" WHERE ").append(whereClause); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + } - PostgresResult result = pg_query(env, conn, query.toString()); + return false; + } - return pg_fetch_all(env, result); + /** + * Sends a request to create a prepared statement with the given parameters, + * without waiting for completion + */ + public static boolean pg_send_prepare(Env env, + @NotNull Postgres conn, + String stmtName, + String query) { + try { - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } + // Note: for now, this is the same as pg_prepare. - /** - * Sends a request to execute a prepared statement with given parameters, - * without waiting for the result(s) - */ - public static boolean pg_send_execute(Env env, - @NotNull Postgres conn, - String stmtName, - ArrayValue params) { - try { + PostgresStatement stmt = pg_prepare(env, conn, stmtName, query); - // Note: for now, this is essentially the same as pg_execute. + conn.setAsynchronousStatement(stmt); - PostgresResult result = pg_execute(env, conn, stmtName, params); + if (stmt != null) { + return true; + } - conn.setAsynchronousResult(result); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + } + + return false; + } + + /** + * Submits a command and separate parameters to the server + * without waiting for the result(s) + */ + public static boolean pg_send_query_params(Env env, + @NotNull Postgres conn, + String query, + ArrayValue params) { + try { + + PostgresStatement pstmt = conn.prepare(env, env.createString(query)); + + return executeInternal(env, conn, pstmt, params) != null; + + /* + ArrayValueImpl arrayImpl = (ArrayValueImpl)params; + int sz = arrayImpl.size(); + + for (int i=0; i 0) { - throw new UnimplementedException("pg_update with options"); - } - - StringBuilder values = new StringBuilder(); - - boolean isFirst = true; - - for (Map.Entry entry : data.entrySet()) { - Value k = entry.getKey(); - Value v = entry.getValue(); - if (isFirst) { - isFirst = false; + ArrayValue arr = pg_fetch_row(env, result, LongValue.ZERO); + + String prevVerbosity = arr.get(LongValue.ZERO).toString(); + + switch (intVerbosity) { + case PGSQL_ERRORS_TERSE: + verbosity = "TERSE"; + break; + case PGSQL_ERRORS_VERBOSE: + verbosity = "VERBOSE"; + break; + default: + verbosity = "DEFAULT"; + } + + pg_query(env, conn, "SET log_error_verbosity TO '" + verbosity + "'"); + + if (prevVerbosity.equals("TERSE")) { + return PGSQL_ERRORS_TERSE; + } else if (prevVerbosity.equals("VERBOSE")) { + return PGSQL_ERRORS_VERBOSE; } else { - values.append(", "); + return PGSQL_ERRORS_DEFAULT; } - values.append(k.toString()); - values.append("='"); - values.append(v.toString()); - values.append("'"); - } - StringBuilder whereClause = new StringBuilder(); + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return -1; + } + } + + /** + * Enable tracing a PostgreSQL connection + */ + public static boolean pg_trace(Env env, + Path path, + @Optional String mode, + @Optional Postgres conn) { + env.stub("pg_trace"); + + return false; + } + + /** + * Returns the current in-transaction status of the server + */ + public static int pg_transaction_status(Env env, + @Optional Postgres conn) { + return PGSQL_TRANSACTION_IDLE; + } + + /** + * Return the TTY name associated with the connection + */ + public static String pg_tty(Env env, + @Optional Postgres conn) { + // Note: pg_tty() is obsolete, since the server no longer pays attention to + // the TTY setting, but the function remains for backwards compatibility. + + env.stub("pg_tty"); + + return ""; + } + + /** + * Unescape binary for bytea type + */ + @ReturnNullAsFalse + public static String pg_unescape_bytea(Env env, + String data) { + try { + + byte[] dataBytes = data.getBytes(); + + Class cl = Class.forName("org.postgresql.util.PGbytea"); + + Method method = cl.getDeclaredMethod( + "toBytes", new Class[]{byte[].class}); + + return new String((byte[]) method.invoke(cl, new Object[]{dataBytes})); + + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } + + /** + * Disable tracing of a PostgreSQL connection + */ + public static boolean pg_untrace(Env env, + @Optional Postgres conn) { + // Always returns TRUE + + env.stub("pg_untrace"); + + return true; + } + + /** + * Update table + */ + public static boolean pg_update(Env env, + @NotNull Postgres conn, + String tableName, + ArrayValue data, + ArrayValue condition, + @Optional int options) { + // From php.net: This function is EXPERIMENTAL. + + // The behaviour of this function, the name of this function, and + // anything else documented about this function may change without + // notice in a future release of PHP. Use this function at your own risk. + + try { + + if (options > 0) { + throw new UnimplementedException("pg_update with options"); + } - isFirst = true; + StringBuilder values = new StringBuilder(); + + boolean isFirst = true; + + for (Map.Entry entry : data.entrySet()) { + Value k = entry.getKey(); + Value v = entry.getValue(); + if (isFirst) { + isFirst = false; + } else { + values.append(", "); + } + values.append(k.toString()); + values.append("='"); + values.append(v.toString()); + values.append("'"); + } - for (Map.Entry entry : condition.entrySet()) { - Value k = entry.getKey(); - Value v = entry.getValue(); - if (isFirst) { - isFirst = false; - } else { - whereClause.append(" AND "); + StringBuilder whereClause = new StringBuilder(); + + isFirst = true; + + for (Map.Entry entry : condition.entrySet()) { + Value k = entry.getKey(); + Value v = entry.getValue(); + if (isFirst) { + isFirst = false; + } else { + whereClause.append(" AND "); + } + whereClause.append(k.toString()); + whereClause.append("='"); + whereClause.append(v.toString()); + whereClause.append("'"); } - whereClause.append(k.toString()); - whereClause.append("='"); - whereClause.append(v.toString()); - whereClause.append("'"); - } - StringBuilder query = new StringBuilder(); - query.append("UPDATE "); - query.append(tableName); - query.append(" SET "); - query.append(values); - query.append(" WHERE "); - query.append(whereClause); + StringBuilder query = new StringBuilder(); + query.append("UPDATE "); + query.append(tableName); + query.append(" SET "); + query.append(values); + query.append(" WHERE "); + query.append(whereClause); - pg_query(env, conn, query.toString()); + pg_query(env, conn, query.toString()); - return true; + return true; - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return false; - } - } + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return false; + } + } - /** - * Returns an array with client, protocol and server version (when available) - */ - @ReturnNullAsFalse - public static ArrayValue pg_version(Env env, - @Optional Postgres conn) { - try { + /** + * Returns an array with client, protocol and server version (when available) + */ + @ReturnNullAsFalse + public static ArrayValue pg_version(Env env, + @Optional Postgres conn) { + try { - //@todo return an array + //@todo return an array - if (conn == null) { - conn = (Postgres) env.getSpecialValue("caucho.postgres"); - } + if (conn == null) { + conn = (Postgres) env.getSpecialValue("caucho.postgres"); + } - ArrayValue result = new ArrayValueImpl(); + ArrayValue result = new ArrayValueImpl(); - result.append(env.createString("client"), - env.createString(conn.getClientInfo())); - result.append(env.createString("server_version"), - env.createString(conn.getServerInfo())); + result.append(env.createString("client"), + env.createString(conn.getClientInfo())); + result.append(env.createString("server_version"), + env.createString(conn.getServerInfo())); - return result; - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } + return result; + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } - private static Postgres getConnection(Env env) { - Postgres conn = (Postgres) env.getSpecialValue("caucho.postgres"); + private static Postgres getConnection(Env env) { + Postgres conn = (Postgres) env.getSpecialValue("caucho.postgres"); - if (conn != null) { - return conn; - } + if (conn != null) { + return conn; + } - String driver = "org.postgresql.Driver"; - String url = "jdbc:postgresql://localhost:5432/"; + String driver = "org.postgresql.Driver"; + String url = "jdbc:postgresql://localhost:5432/"; - conn = new Postgres(env, "localhost", "", "", "", 5432, driver, url); + conn = new Postgres(env, "localhost", "", "", "", 5432, driver, url); - env.setSpecialValue("caucho.postgres", conn); + env.setSpecialValue("caucho.postgres", conn); - return conn; - } + return conn; + } - private static PostgresResult executeInternal(Env env, - @NotNull Postgres conn, - PostgresStatement pstmt, - ArrayValue params) { - try { + private static PostgresResult executeInternal(Env env, + @NotNull Postgres conn, + PostgresStatement pstmt, + ArrayValue params) { + try { - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder stringBuilder = new StringBuilder(); - int size = params.getSize(); // pstmt.getPreparedMappingSize(); + int size = params.getSize(); // pstmt.getPreparedMappingSize(); - for (int i = 0; i < size; i++) { - stringBuilder.append('s'); - } + for (int i = 0; i < size; i++) { + stringBuilder.append('s'); + } - String types = stringBuilder.toString(); + String types = stringBuilder.toString(); - Value[] value = params.valuesToArray(); - pstmt.bindParams(env, types, value); + Value[] value = params.valuesToArray(); + pstmt.bindParams(env, types, value); - if (!pstmt.execute(env)) { - return null; - } + if (!pstmt.execute(env)) { + return null; + } - if (pstmt.getStatementType().equals("SELECT")) { - PostgresResult result = new PostgresResult( - env, null, pstmt.getResultSet(), null); - conn.setResultResource(result); - return result; - } else { - // TODO: ??? return type? - return null; - // return pstmt; - } + if (pstmt.getStatementType().equals("SELECT")) { + PostgresResult result = new PostgresResult( + env, null, pstmt.getResultSet(), null); + conn.setResultResource(result); + return result; + } else { + // TODO: ??? return type? + return null; + // return pstmt; + } - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return null; - } - } + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return null; + } + } - private static int writeLobInternal(Object largeObject, - InputStream is, - int len) { - try { + private static int writeLobInternal(Object largeObject, + InputStream is, + int len) { + try { - Class cl = Class.forName("org.postgresql.largeobject.LargeObject"); + Class cl = Class.forName("org.postgresql.largeobject.LargeObject"); - Method method = cl.getDeclaredMethod("getOutputStream", (Class) null); + Method method = cl.getDeclaredMethod("getOutputStream", (Class) null); - OutputStream os = (OutputStream) method.invoke( - largeObject, new Object[]{}); + OutputStream os = (OutputStream) method.invoke( + largeObject, new Object[]{}); - int written = 0; + int written = 0; - int b; + int b; - while (((b = is.read()) >= 0) && (written++ < len)) { - os.write(b); - } + while (((b = is.read()) >= 0) && (written++ < len)) { + os.write(b); + } - os.close(); + os.close(); - return written; + return written; - } catch (Exception ex) { - log.log(Level.FINE, ex.toString(), ex); - return -1; - } - } + } catch (Exception ex) { + log.log(Level.FINE, ex.toString(), ex); + return -1; + } + } } diff --git a/quercus/src/main/java/com/caucho/quercus/php/BadFunctionCallException.php b/quercus/src/main/java/com/caucho/quercus/php/BadFunctionCallException.php new file mode 100755 index 0000000..cffc5b8 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/BadFunctionCallException.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/quercus/src/main/java/com/caucho/quercus/php/BadMethodCallException.php b/quercus/src/main/java/com/caucho/quercus/php/BadMethodCallException.php new file mode 100755 index 0000000..256e542 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/BadMethodCallException.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/quercus/src/main/java/com/caucho/quercus/php/DomainException.php b/quercus/src/main/java/com/caucho/quercus/php/DomainException.php new file mode 100755 index 0000000..e6428d8 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/DomainException.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/quercus/src/main/java/com/caucho/quercus/php/ErrorException.php b/quercus/src/main/java/com/caucho/quercus/php/ErrorException.php new file mode 100755 index 0000000..bf977e1 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/ErrorException.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/quercus/src/main/java/com/caucho/quercus/php/FilterIterator.php b/quercus/src/main/java/com/caucho/quercus/php/FilterIterator.php new file mode 100755 index 0000000..ad1a1b9 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/FilterIterator.php @@ -0,0 +1,49 @@ +_current; + } + + function fetch() + { + $this->_key = $this->it->key(); + $this->_current = $this->it->current(); + + for (; $this->it->valid() && ! $this->accept(); $this->it->next()) { + $this->_key = $this->it->key(); + $this->_current = $this->it->current(); + } + } + + function key() + { + return $this->_key; + } + + function next() + { + parent::next(); + $this->fetch(); + } + + function rewind() + { + parent::rewind(); + $this->fetch(); + } + + function __call($fun, $param) + { + return call_user_func_array(array($this->it, $fun), $param); + } +} diff --git a/quercus/src/main/java/com/caucho/quercus/php/InvalidArgumentException.php b/quercus/src/main/java/com/caucho/quercus/php/InvalidArgumentException.php new file mode 100755 index 0000000..b639a51 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/InvalidArgumentException.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/quercus/src/main/java/com/caucho/quercus/php/IteratorIterator.php b/quercus/src/main/java/com/caucho/quercus/php/IteratorIterator.php new file mode 100755 index 0000000..c2e30b0 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/IteratorIterator.php @@ -0,0 +1,40 @@ +it = $it; + } + + function getInnerIterator() + { + return $this->it; + } + + function current() + { + return $this->it->current(); + } + + function key() + { + return $this->it->key(); + } + + function next() + { + $this->it->next(); + } + + function rewind() + { + $this->it->rewind(); + } + + function valid() + { + return $this->it->valid(); + } +} diff --git a/quercus/src/main/java/com/caucho/quercus/php/LengthException.php b/quercus/src/main/java/com/caucho/quercus/php/LengthException.php new file mode 100755 index 0000000..24bd95e --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/LengthException.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/quercus/src/main/java/com/caucho/quercus/php/LogicException.php b/quercus/src/main/java/com/caucho/quercus/php/LogicException.php new file mode 100755 index 0000000..d0fd958 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/LogicException.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/quercus/src/main/java/com/caucho/quercus/php/OutOfBoundsException.php b/quercus/src/main/java/com/caucho/quercus/php/OutOfBoundsException.php new file mode 100755 index 0000000..37c6af8 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/OutOfBoundsException.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/quercus/src/main/java/com/caucho/quercus/php/OutOfRangeException.php b/quercus/src/main/java/com/caucho/quercus/php/OutOfRangeException.php new file mode 100755 index 0000000..ceb8490 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/OutOfRangeException.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/quercus/src/main/java/com/caucho/quercus/php/OverflowException.php b/quercus/src/main/java/com/caucho/quercus/php/OverflowException.php new file mode 100755 index 0000000..8062933 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/OverflowException.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/quercus/src/main/java/com/caucho/quercus/php/RangeException.php b/quercus/src/main/java/com/caucho/quercus/php/RangeException.php new file mode 100755 index 0000000..45a9688 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/RangeException.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/quercus/src/main/java/com/caucho/quercus/php/RecursiveIteratorIterator.php b/quercus/src/main/java/com/caucho/quercus/php/RecursiveIteratorIterator.php new file mode 100755 index 0000000..a0cd40e --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/RecursiveIteratorIterator.php @@ -0,0 +1,112 @@ +ait = $it; + $this->mode = $mode; + $this->flags = $flags; + } + + function getInnerIterator() + { + + } + + function current() + { + + } + + function getDepth() + { + return $this->level; + } + + function getMaxDepth() + { + + } + + function setMaxDepth() + { + + } + + function getSubIterator() + { + + } + + function beginIteration() + { + + } + + function callHasChildren() + { + + } + + function callGetChildren() + { + + } + + function beginChildren() + { + + } + + function endChildren() + { + + } + + function endIteration() + { + + } + + private function callNextElement($after_move) + { + + } + + function nextElement() + { + + } + + function key() + { + + } + + function next() + { + + } + + function rewind() + { + + } + + function valid() + { + + } +} diff --git a/quercus/src/main/java/com/caucho/quercus/php/RuntimeException.php b/quercus/src/main/java/com/caucho/quercus/php/RuntimeException.php new file mode 100755 index 0000000..c6d96b1 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/RuntimeException.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/quercus/src/main/java/com/caucho/quercus/php/UnderflowException.php b/quercus/src/main/java/com/caucho/quercus/php/UnderflowException.php new file mode 100755 index 0000000..a321d24 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/UnderflowException.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/quercus/src/main/java/com/caucho/quercus/php/UnexpectedValueException.php b/quercus/src/main/java/com/caucho/quercus/php/UnexpectedValueException.php new file mode 100755 index 0000000..07ca495 --- /dev/null +++ b/quercus/src/main/java/com/caucho/quercus/php/UnexpectedValueException.php @@ -0,0 +1,7 @@ + \ No newline at end of file