/*
* Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.quercus.lib;
import com.caucho.quercus.QuercusModuleException;
import com.caucho.quercus.annotation.Optional;
import com.caucho.quercus.annotation.PassThru;
import com.caucho.quercus.annotation.ReadOnly;
import com.caucho.quercus.annotation.Reference;
import com.caucho.quercus.annotation.ReturnNullAsFalse;
import com.caucho.quercus.annotation.UsesSymbolTable;
import com.caucho.quercus.env.*;
import com.caucho.quercus.module.AbstractQuercusModule;
import com.caucho.util.L10N;
import com.caucho.util.LruCache;
import com.caucho.vfs.StringWriter;
import com.caucho.vfs.WriteStream;
import java.io.IOException;
import java.lang.ref.*;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Information about PHP variables.
*/
public class VariableModule extends AbstractQuercusModule {
private static final Logger log
= Logger.getLogger(VariableModule.class.getName());
private static final L10N L = new L10N(VariableModule.class);
private static final
LruCache<UnserializeKey,UnserializeCacheEntry> _unserializeCache
= new LruCache<UnserializeKey,UnserializeCacheEntry>(256);
/**
* Returns a constant
*
* @param env the quercus calling environment
* @param name the constant name
*/
public static Value constant(Env env, String name)
{
if (name == null) {
env.warning(L.l("null passed as constant name"));
return NullValue.NULL;
}
int i = name.indexOf("::");
if (i > 0) {
String cls = name.substring(0, i);
name = name.substring(i + 2);
return env.getClass(cls).getConstant(env, name);
}
else
return env.getConstant(name);
}
/**
* Prints a debug version of the variable
*
* @param env the quercus calling environment
* @param v the variable to print
* @return the escaped stringPhp
*/
public static Value debug_zval_dump(Env env, @ReadOnly Value v)
{
try {
debug_impl(env, v, 0);
return NullValue.NULL;
} catch (IOException e) {
throw new QuercusModuleException(e);
}
}
/**
* Defines a constant
*
* @param env the quercus calling environment
* @param name the constant name
* @param value the constant value
*/
public static Value define(Env env,
StringValue name,
Value value,
@Optional boolean isCaseInsensitive)
{
return env.addConstant(name, value, isCaseInsensitive);
}
/**
* Returns true if the constant is defined.
*
* @param env the quercus calling environment
* @param name the constant name
*/
public static boolean defined(Env env, String name)
{
if (name == null)
return false;
int i = name.indexOf("::");
if (i > 0) {
String clsName = name.substring(0, i);
name = name.substring(i + 2);
QuercusClass cls = env.getClass(clsName);
return cls.hasConstant(name);
}
else
return env.isDefined(name);
}
/**
* Converts to a double
*
* @param v the variable to convert
* @return the double value
*/
public static Value doubleval(@ReadOnly Value v)
{
return floatval(v);
}
/**
* Returns true for an empty variable.
*
* @param v the value to test
*
* @return true if the value is empty
*/
public static boolean empty(@ReadOnly Value v)
{
return v.isEmpty();
}
/**
* Converts to a double
*
* @param v the variable to convert
* @return the double value
*/
public static Value floatval(@ReadOnly Value v)
{
return new DoubleValue(v.toDouble());
}
/**
* Returns the defined variables in the current scope.
*/
@UsesSymbolTable
public static Value get_defined_vars(Env env)
{
ArrayValue result = new ArrayValueImpl();
Map<String,EnvVar> map = env.getEnv();
for (Map.Entry<String,EnvVar> entry : map.entrySet()) {
result.append(env.createStringOld(entry.getKey()),
entry.getValue().get());
}
Map<String,EnvVar> globalMap = env.getGlobalEnv();
if (map != globalMap) {
for (Map.Entry<String,EnvVar> entry : globalMap.entrySet()) {
result.append(env.createStringOld(entry.getKey()),
entry.getValue().get());
}
}
return result;
}
/*
* Returns the type of this resource.
*/
@ReturnNullAsFalse
public static String get_resource_type(Env env, Value v)
{
return v.getResourceType();
}
/**
* Returns the type string for the variable
*/
public static String gettype(@ReadOnly Value v)
{
return v.getType();
}
/**
* Imports request variables
*
* @param types the variables to import
* @param prefix the prefix
*/
public static boolean import_request_variables(Env env,
String types,
@Optional String prefix)
{
if ("".equals(prefix))
env.notice(L.l("import_request_variables should use a prefix argument"));
for (int i = 0; i < types.length(); i++) {
char ch = types.charAt(i);
Value value = null;
if (ch == 'c' || ch == 'C')
value = env.getGlobalValue("_COOKIE");
else if (ch == 'g' || ch == 'G')
value = env.getGlobalValue("_GET");
else if (ch == 'p' || ch == 'P')
value = env.getGlobalValue("_POST");
if (! (value instanceof ArrayValue))
continue;
ArrayValue array = (ArrayValue) value;
for (Map.Entry<Value,Value> entry : array.entrySet()) {
String key = entry.getKey().toString();
env.setGlobalValue(prefix + key,
array.getRef(entry.getKey()));
}
}
return true;
}
/**
* Converts to a long
*
* @param v the variable to convert
* @return the double value
*/
public static Value intval(@ReadOnly Value v)
{
return v.toLongValue();
}
/**
* Converts to a long
*
* @param v the variable to convert
* @return the double value
*/
public static long intval(Env env, @ReadOnly Value v, int base)
{
if (! v.isString())
return v.toLong();
StringValue s = v.toStringValue(env);
int len = s.length();
long value = 0;
for (int i = 0; i < len; i++) {
char ch = s.charAt(i);
int digit;
if ('0' <= ch && ch <= '9')
digit = ch - '0';
else if ('a' <= ch && ch <= 'z')
digit = ch - 'a' + 10;
else if ('A' <= ch && ch <= 'Z')
digit = ch - 'A' + 10;
else
digit = 0;
value = value * base + digit;
}
return value;
}
/**
* Returns true for an array.
*
* @param v the value to test
*
* @return true for an array
*/
public static boolean is_array(@ReadOnly Value v)
{
return v.isArray();
}
// XXX: is_binary
/**
* Returns true for a boolean
*
* @param v the value to test
*
* @return true for a boolean
*/
public static Value is_bool(@ReadOnly Value v)
{
return (v.toValue() instanceof BooleanValue
? BooleanValue.TRUE
: BooleanValue.FALSE);
}
// XXX: is_buffer
/**
* Returns the type string for the variable
*/
public static boolean is_callable(Env env,
Value v,
@Optional boolean isSyntaxOnly,
@Optional @Reference Value nameRef)
{
if (v instanceof StringValue) {
if (nameRef != null)
nameRef.set(v);
if (isSyntaxOnly)
return true;
else
return env.findFunction(v.toString()) != null;
}
else if (v instanceof ArrayValue) {
Value obj = v.get(LongValue.ZERO);
Value name = v.get(LongValue.ONE);
if (! (name instanceof StringValue))
return false;
if (nameRef != null)
nameRef.set(name);
if (obj instanceof StringValue) {
if (isSyntaxOnly)
return true;
QuercusClass cl = env.findClass(obj.toString());
if (cl == null)
return false;
return (cl.findFunction(name.toString()) != null);
}
else if (obj.isObject()) {
if (isSyntaxOnly)
return true;
return obj.findFunction(name.toString()) != null;
}
else
return false;
}
else
return false;
}
/**
* Returns true for a double
*
* @param v the value to test
*
* @return true for a double
*/
public static boolean is_double(@ReadOnly Value v)
{
return is_float(v);
}
/**
* Returns true for a double
*
* @param v the value to test
*
* @return true for a double
*/
public static boolean is_float(@ReadOnly Value v)
{
return (v.toValue() instanceof DoubleValue);
}
/**
* Returns true for an integer
*
* @param v the value to test
*
* @return true for a double
*/
public static Value is_int(@ReadOnly Value v)
{
return (v.toValue() instanceof LongValue
? BooleanValue.TRUE
: BooleanValue.FALSE);
}
/**
* Returns true for an integer
*
* @param v the value to test
*
* @return true for a double
*/
public static Value is_integer(@ReadOnly Value v)
{
return is_int(v);
}
/**
* Returns true for an integer
*
* @param v the value to test
*
* @return true for a double
*/
public static Value is_long(@ReadOnly Value v)
{
return is_int(v);
}
/**
* Returns true for null
*
* @param v the value to test
*
* @return true for null
*/
public static boolean is_null(@ReadOnly Value v)
{
return v.isNull();
}
/**
* Returns true for numeric
*
* @param env the calling environment
* @param v the value to test
*
* @return true for numeric
*/
public static boolean is_numeric(Env env, @ReadOnly Value v)
{
return v.isNumeric();
}
/**
* Returns true for an object
*
* @param env the calling environment
* @param v the value to test
*
* @return true for object
*/
public static boolean is_object(Env env, @ReadOnly Value v)
{
return v.isObject();
}
/**
* Returns true for a real
*
* @param v the value to test
*
* @return true for a real
*/
public static boolean is_real(@ReadOnly Value v)
{
return is_float(v);
}
/**
* Returns true if the value is a resource
*/
public boolean is_resource(@ReadOnly Value value)
{
return value.isResource();
}
/**
* Returns true for a scalar
*
* @param v the value to test
*
* @return true for a scalar
*/
public static boolean is_scalar(@ReadOnly Value v)
{
if (v == null)
return false;
Value value = v.toValue();
return (value instanceof DoubleValue
|| value instanceof StringValue
|| value instanceof LongValue
|| value instanceof BooleanValue);
}
/**
* Returns true if the value is a string
*/
public boolean is_string(@ReadOnly Value value)
{
return value.isString();
}
// XXX: is_unicode
/**
* Returns the type string for the variable
*/
public static boolean isset(@ReadOnly Value v)
{
return v.isset();
}
/**
* Prints a value. If isReturn is true, then returns what was supposed
* to be printed as a string instead.
*
* @param env the quercus calling environment
* @param v the variable to print
* @param isReturn set to true if returning instead of printing value
* @return the string that was supposed to be printed, or true
*/
public static Value print_r(Env env,
@ReadOnly Value v,
@Optional boolean isReturn)
{
try {
WriteStream out;
if (isReturn) {
StringWriter writer = new StringWriter();
out = writer.openWrite();
out.setNewlineString("\n");
v.printR(env, out, 0, new IdentityHashMap<Value, String>());
return env.createStringOld(writer.getString());
}
else {
out = env.getOut();
v.printR(env, out, 0, new IdentityHashMap<Value, String>());
return BooleanValue.TRUE;
}
} catch (IOException e) {
throw new QuercusModuleException(e);
}
}
private static void printDepth(WriteStream out, int depth)
throws IOException
{
for (int i = 0; i < depth; i++)
out.print(' ');
}
/**
* Serializes the value to a string.
*/
public static String serialize(Env env,
@PassThru @ReadOnly Value v)
{
StringBuilder sb = new StringBuilder();
v.serialize(env, sb, new SerializeMap());
return sb.toString();
}
/**
* Converts the variable to a specified tyep.
*/
public static boolean settype(Env env,
@Reference Value var,
String type)
{
Value value = var.toValue();
if ("null".equals(type)) {
var.set(NullValue.NULL);
return true;
}
else if ("boolean".equals(type) || "bool".equals(type)) {
var.set(value.toBoolean() ? BooleanValue.TRUE : BooleanValue.FALSE);
return true;
}
else if ("string".equals(type)) {
var.set(value.toStringValue(env));
return true;
}
else if ("int".equals(type) || "integer".equals(type)) {
var.set(LongValue.create(value.toLong()));
return true;
}
else if ("float".equals(type) || "double".equals(type)) {
var.set(new DoubleValue(value.toDouble()));
return true;
}
else if ("object".equals(type)) {
var.set(value.toObject(env));
return true;
}
else if ("array".equals(type)) {
if (value.isArray())
var.set(value);
else {
ArrayValueImpl array = new ArrayValueImpl();
var.set(array);
if (! value.isNull())
array.append(value);
}
return true;
}
else
return false;
}
/**
* Converts to a string
*
* @param env the quercus calling environment
* @param v the variable to convert
* @return the double value
*/
public static Value strval(Env env, @ReadOnly Value v)
{
if (v instanceof StringValue)
return (StringValue) v;
else
return v.toReprString(env);
}
/**
* Unserializes the value from a string.
*/
public static Value unserialize(Env env, StringValue s)
{
Value v = null;
UnserializeKey key = new UnserializeKey(s);
UnserializeCacheEntry entry = _unserializeCache.get(key);
if (entry != null) {
v = entry.getValue(env);
if (v != null)
return v;
}
UnserializeReader is = null;
try {
is = new UnserializeReader(s);
v = is.unserialize(env);
} catch (IOException e) {
log.log(Level.FINE, e.toString(), e);
env.notice(e.toString());
v = BooleanValue.FALSE;
}
if (is != null && ! is.useReference()) {
entry = new UnserializeCacheEntry(v);
_unserializeCache.put(key, entry);
return entry.getValue(env);
}
return v;
}
// XXX: unset
/**
* Prints a debug version of the variable
*
* @param env the quercus calling environment
* @param v the variable to print
* @return the escaped stringPhp
*/
public static Value var_dump(Env env,
@PassThru @ReadOnly Value v,
Value []args)
{
try {
if (v == null)
env.getOut().print("NULL#java");
else {
v.varDump(env, env.getOut(), 0, new IdentityHashMap<Value,String>());
env.getOut().println();
}
if (args != null) {
for (Value value : args) {
if (value == null)
env.getOut().print("NULL#java");
else {
value.varDump(env, env.getOut(), 0,
new IdentityHashMap<Value,String>());
env.getOut().println();
}
}
}
return NullValue.NULL;
} catch (IOException e) {
throw new QuercusModuleException(e);
}
}
/**
* Serializes the value to a string.
*/
public static Value var_export(Env env,
@ReadOnly Value v,
@Optional boolean isReturn)
{
StringBuilder sb = new StringBuilder();
v.varExport(sb);
if (isReturn)
return env.createStringOld(sb.toString());
else {
env.print(sb);
return NullValue.NULL;
}
}
private static void debug_impl(Env env, Value v, int depth)
throws IOException
{
WriteStream out = env.getOut();
if (v instanceof Var)
out.print("&");
v = v.toValue();
if (v instanceof ArrayValue) {
ArrayValue array = (ArrayValue) v;
out.println("Array");
printDepth(out, 2 * depth);
out.println("(");
for (Map.Entry<Value,Value> entry : array.entrySet()) {
printDepth(out, 2 * depth);
out.print(" [");
out.print(entry.getKey());
out.print("] => ");
debug_impl(env, entry.getValue(), depth + 1); // XXX: recursion
}
printDepth(out, 2 * depth);
out.println(")");
}
else if (v instanceof BooleanValue) {
if (v.toBoolean())
out.print("bool(true)");
else
out.print("bool(false)");
}
else if (v instanceof LongValue) {
out.print("int(" + v.toLong() + ")");
}
else if (v instanceof DoubleValue) {
out.print("float(" + v.toDouble() + ")");
}
else if (v instanceof StringValue) {
out.print("string(" + v.toString() + ")");
}
else if (v instanceof NullValue) {
out.print("NULL");
}
else {
v.print(env);
}
}
static class UnserializeKey {
private final SoftReference<StringValue> _stringRef;
private int _hash;
UnserializeKey(StringValue string)
{
_hash = string.hashCode();
_stringRef = new SoftReference<StringValue>(string);
}
public int hashCode()
{
return _hash;
}
public boolean equals(Object o)
{
if (this == o)
return true;
else if (! (o instanceof UnserializeKey))
return false;
UnserializeKey key = (UnserializeKey) o;
StringValue a = _stringRef.get();
StringValue b = key._stringRef.get();
if (a == null || b == null)
return false;
return a.equals(b);
}
}
}