package php.runtime.reflection; import php.runtime.Memory; import php.runtime.common.HintType; import php.runtime.env.Context; import php.runtime.env.Environment; import php.runtime.env.TraceInfo; import php.runtime.exceptions.CriticalException; import php.runtime.exceptions.support.ErrorType; import php.runtime.invoke.Invoker; import php.runtime.lang.BaseWrapper; import php.runtime.lang.IObject; import php.runtime.memory.ObjectMemory; import php.runtime.memory.support.MemoryOperation; import php.runtime.reflection.support.Entity; import php.runtime.reflection.support.ReflectionUtils; import php.runtime.reflection.support.TypeChecker; import php.runtime.util.JVMStackTracer; public class ParameterEntity extends Entity { public interface TypeHintingChecker { boolean call(Environment env, Memory value); String getNeeded(Environment env, Memory value); } protected ClassEntity clazz; protected Memory defaultValue; protected String defaultValueConstName; protected boolean isReference; protected TypeChecker typeChecker; protected TypeHintingChecker typeHintingChecker; protected boolean mutable = true; protected boolean used = true; protected boolean nullable = false; protected boolean variadic = false; public ParameterEntity(Context context) { super(context); } public String getDefaultValueConstName() { return defaultValueConstName; } public void setDefaultValueConstName(String defaultValueConstName) { this.defaultValueConstName = defaultValueConstName; } public Memory getDefaultValue() { return defaultValue; } public void setDefaultValue(Memory defaultValue) { this.defaultValue = defaultValue; } public ClassEntity getClazz() { return clazz; } private void setClazz(ClassEntity clazz) { this.clazz = clazz; } public boolean isReference() { return isReference; } public void setReference(boolean reference) { isReference = reference; } public TypeChecker getTypeChecker() { return typeChecker; } public void setTypeChecker(TypeChecker typeChecker) { this.typeChecker = typeChecker; } public HintType getType() { return typeChecker instanceof TypeChecker.Simple ? ((TypeChecker.Simple) typeChecker).getType() : HintType.ANY; } public String getTypeClass() { return typeChecker instanceof TypeChecker.ClassName ? ((TypeChecker.ClassName) typeChecker).getTypeClass() : null; } public String getTypeClassLower() { return typeChecker instanceof TypeChecker.ClassName ? ((TypeChecker.ClassName) typeChecker).getTypeClassLower() : null; } public void setType(HintType type) { this.typeChecker = type == null ? null : TypeChecker.of(type); } public boolean isNullable() { return nullable; } public void setNullable(boolean nullable) { this.nullable = nullable; } public void setTypeClass(String typeClass) { typeChecker = typeClass == null ? null : TypeChecker.of(typeClass); } public void setTypeNativeClass(Class<?> typeNativeClass) { Class<?> baseWrapper = MemoryOperation.getWrapper(typeNativeClass); if (baseWrapper == null) { throw new CriticalException("Support only wrapper classes"); } typeChecker = TypeChecker.of(ReflectionUtils.getClassName(baseWrapper)); } public void setType(String type) { HintType _type = HintType.of(type); this.typeChecker = _type == null ? null : TypeChecker.of(_type); } public Class<? extends Enum> getTypeEnum() { return typeChecker instanceof TypeChecker.EnumClass ? ((TypeChecker.EnumClass) typeChecker).getTypeEnum() : null; } public void setTypeEnum(Class<? extends Enum> typeEnum) { this.typeChecker = typeEnum == null ? null : TypeChecker.ofEnum(typeEnum); } public TypeHintingChecker getTypeHintingChecker() { return typeHintingChecker; } public void setTypeHintingChecker(TypeHintingChecker typeHintingChecker) { this.typeHintingChecker = typeHintingChecker; } public static void validateTypeHinting(Environment env, int index, Memory[] args, HintType type, boolean nullable) { Memory value = args[index - 1]; if (!checkTypeHinting(env, value, type, nullable)) { String given; if (value == null){ given = "none"; } else if (value.isObject()) { given = "instance of " + value.toValue(ObjectMemory.class).getReflection().getName(); } else { given = value.getRealType().toString(); } StackTraceElement[] stack = Thread.currentThread().getStackTrace(); StackTraceElement e = stack[2]; StackTraceElement where = stack[3]; TraceInfo trace; if (where.getLineNumber() <= 0) trace = env.trace(); else trace = new TraceInfo(where); JVMStackTracer.Item item = new JVMStackTracer.Item(env.scope.getClassLoader(), e); env.error(trace, ErrorType.E_RECOVERABLE_ERROR, "Argument %s passed to %s() must be of the type %s, %s given", index, item.getSignature(), type.toString(), given ); } } public static boolean checkTypeHinting(Environment env, Memory value, HintType type) { return checkTypeHinting(env, value, type, false); } public static boolean checkTypeHinting(Environment env, Memory value, HintType type, boolean nullable) { if (nullable && value.isNull()) return true; return TypeChecker.of(type).check(env, value, nullable); } public boolean checkTypeHinting(Environment env, Memory value, String typeClass, boolean nullable) { if (nullable && value.isNull()) return true; if (!value.isObject()) return false; return TypeChecker.of(typeClass).check(env, value, nullable); } public boolean checkTypeHinting(Environment env, Memory value) { if (typeChecker != null) { return typeChecker.check(env, value, nullable || (defaultValue != null && defaultValue.isNull())); } return true; } public boolean isArray(){ return getType() == HintType.ARRAY; } public boolean isCallable(){ return getType() == HintType.CALLABLE; } public boolean isOptional(){ return defaultValue != null; } public boolean isDefaultValueAvailable(){ return defaultValue != null; } public boolean canBePassedByValue(){ return !isReference; } public boolean isPassedByReference(){ return isReference; } public String getSignatureString(){ StringBuilder sb = new StringBuilder(); if (typeChecker != null) { String signature = typeChecker.getSignature(); if (signature != null && !signature.isEmpty()) { sb.append(signature).append(" "); } } if (isReference) sb.append("&"); sb.append("$").append(name); return sb.toString(); } public boolean isMutable() { return mutable; } public void setMutable(boolean mutable) { this.mutable = mutable; } public boolean isUsed() { return used; } public void setUsed(boolean used) { this.used = used; } public boolean isVariadic() { return variadic; } public void setVariadic(boolean variadic) { this.variadic = variadic; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ParameterEntity)) return false; if (!super.equals(o)) return false; ParameterEntity that = (ParameterEntity) o; if (isReference != that.isReference) return false; if (clazz != null ? !clazz.equals(that.clazz) : that.clazz != null) return false; return getType() == that.getType(); } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (clazz != null ? clazz.hashCode() : 0); result = 31 * result + (isReference ? 1 : 0); result = 31 * result + (getType() != null ? getType().hashCode() : 0); return result; } }