/// <summary> /// Creates a new PHP stack frame. /// </summary> /// <param name="context">A script context.</param> /// <param name="frame">The respective CLR frame.</param> /// <param name="kind">A kind of the frame.</param> internal PhpStackFrame(ScriptContext /*!*/ context, StackFrame /*!*/ frame, FrameKinds kind) { Debug.Assert(context != null && frame != null && kind != FrameKinds.Invisible); this.frame = frame; this.kind = kind; MethodBase method = frame.GetMethod(); if (kind == FrameKinds.ClassLibraryFunction) { this.name = ImplementsFunctionAttribute.Reflect(method).Name; SetDebugInfo(frame); } else { Type type = method.DeclaringType; int eval_id = TransientAssembly.InvalidEvalId; if (type != null && context.ApplicationContext.IsTransientRealType(type)) { // gets [PhpEvalId] attribute defined on the type: object[] attrs = type.GetCustomAttributes(typeof(PhpEvalIdAttribute), false); eval_id = ((PhpEvalIdAttribute)attrs[0]).Id; ErrorStackInfo info = new ErrorStackInfo(); PhpStackTrace.FillEvalStackInfo(context, eval_id, ref info, false); this.line = info.Line; this.column = info.Column; this.file = info.File; this.name = info.Caller; } else { SetDebugInfo(frame); } // the caller has already been set by FillEvalStackInfo // if it is not an eval main: if (!(eval_id != TransientAssembly.InvalidEvalId && PhpScript.IsScriptType(type) && kind == FrameKinds.Main)) { if (this.name == ScriptModule.MainHelperName) { this.name = "{main}"; } else { int j; PhpScript.ParseMDeclName(method.Name, out this.name, out j); } } } }
/// <summary> /// Invoked when the instance is created (not called when unserialized). /// </summary> protected override void InstanceCreated(ScriptContext context) { base.InstanceCreated(context); PhpStackTrace trace = new PhpStackTrace(context, 1); PhpStackFrame frame = trace.GetFrame(0); Debug.Assert(frame != null); this.file.Value = frame.File; this.line.Value = frame.Line; this.column.Value = frame.Column; this.trace.Value = trace.GetUserTrace(); }
/// <summary> /// Attempts to bind this callback to its target. /// </summary> /// <param name="quiet"><B>true</B> of no errors should be thrown, <B>false</B> otherwise.</param> /// <param name="nameContext">Current <see cref="NamingContext"/> for function and class name resolution.</param> /// <param name="caller">Current class context or a <see cref="UnknownTypeDesc"/> if the class context /// should be determined ad-hoc.</param> /// <returns><B>True</B> if the callback was successfully bound, <B>false</B> if an error occured.</returns> public bool Bind(bool quiet, DTypeDesc caller, NamingContext nameContext) { if (IsInvalid) { return(false); } switch (state) { case State.UnboundFunction: { if (context == null) { context = ScriptContext.CurrentContext; } routineDesc = context.ResolveFunction(targetName, nameContext, quiet); if (routineDesc == null) { return(false); } state = State.Bound; return(true); } case State.UnboundStaticMethod: { if (context == null) { context = ScriptContext.CurrentContext; } if (caller != null && caller.IsUnknown) { callingContext = PhpStackTrace.GetClassContext(); } else { callingContext = caller; } // try to find the CLR method // find the class according to className ResolveTypeFlags flags = ResolveTypeFlags.UseAutoload; if (!quiet) { flags |= ResolveTypeFlags.ThrowErrors; } DTypeDesc type = context.ResolveType(className, nameContext, callingContext, null, flags); if (type == null) { return(false); } // find the method bool is_caller_method; lateStaticBindType = type; routineDesc = Operators.GetStaticMethodDesc(type, targetName, ref instance, callingContext, context, quiet, false, out is_caller_method); if (routineDesc == null) { return(false); } if (instance != null) { dummyInstance = true; } state = is_caller_method ? State.BoundToCaller : State.Bound; return(true); } case State.UnboundInstanceMethod: { if (caller != null && caller.IsUnknown) { callingContext = PhpStackTrace.GetClassContext(); } else { callingContext = caller; } // ask the instance for a handle to the method bool is_caller_method; routineDesc = instance.GetMethodDesc(targetName, callingContext, quiet, out is_caller_method); if (routineDesc == null) { return(false); } state = (is_caller_method ? State.BoundToCaller : State.Bound); return(true); } } return(true); }
/// <summary> /// Reports a PHP error. /// </summary> /// <param name="error">The error type</param> /// <param name="message">The error message.</param> public static void Throw(PhpError error, string message) { if (ThrowCallbackOverride != null) { ThrowCallbackOverride(error, message); return; } ErrorStackInfo info = new ErrorStackInfo(); bool info_loaded = false; // gets the current script context and config: ScriptContext context = ScriptContext.CurrentContext; LocalConfiguration config = context.Config; // determines whether the error will be reported and whether it is handleable: bool is_error_reported = ((PhpErrorSet)error & config.ErrorControl.ReportErrors) != 0 && !context.ErrorReportingDisabled; bool is_error_handleable = ((PhpErrorSet)error & PhpErrorSet.Handleable & (PhpErrorSet)config.ErrorControl.UserHandlerErrors) != 0; bool is_error_fatal = ((PhpErrorSet)error & PhpErrorSet.Fatal) != 0; bool do_report = true; // remember last error info context.LastErrorType = error; context.LastErrorMessage = message; context.LastErrorFile = null; // only if we are getting ErrorStackInfo, see PhpStackTrace.TraceErrorFrame context.LastErrorLine = 0; // only if we are getting ErrorStackInfo, see PhpStackTrace.TraceErrorFrame // calls a user defined handler if available: if (is_error_handleable && config.ErrorControl.UserHandler != null) { // loads stack info: Func <ErrorStackInfo> func = () => { if (!info_loaded) { info = PhpStackTrace.TraceErrorFrame(context, true); info_loaded = true; } return(info); }; do_report = CallUserErrorHandler(context, error, func, message); } // reports error to output and logs: if (do_report && is_error_reported && (config.ErrorControl.DisplayErrors || config.ErrorControl.EnableLogging)) // check if the error will be displayed to avoid stack trace loading { // loads stack info: if (!info_loaded) { info = PhpStackTrace.TraceErrorFrame(context, false); info_loaded = true; } ReportError(config, context.Output, error, -1, info, message); } // Throws an exception if the error is fatal and throwing is enabled. // PhpError.UserError is also fatal, but can be cancelled by user handler => handler call must precede this line. // Error displaying must also precede this line because the error should be displayed before an exception is thrown. if (is_error_fatal && context.ThrowExceptionOnError) { // loads stack info: if (!info_loaded) { info = PhpStackTrace.TraceErrorFrame(context, false); info_loaded = true; } throw new PhpException(error, message, info); } }