/// <summary> /// Resolves a method of given <see cref="DType"/> by its name. /// </summary> /// <param name="type">The type of routine being resolved.</param> /// <param name="methodName">The name of routine to be resolved.</param> /// <param name="position">Position of method call used for error reporting.</param> /// <param name="referringType">The type where the seached routine is being called. Can be <c>null</c>.</param> /// <param name="referringRoutine">The routine where the searched routine is being called. Can be <c>null</c>.</param> /// <param name="calledStatically">True if the searched routine is called statically - if it uses static method call syntax. /// This affects the __call or __callStatic method lookup. /// It affects also the error reporting, where for instance method calls, the bad visibility error is /// ignored and falls back to return <see cref="UnknownMethod"/>.</param> /// <param name="checkVisibilityAtRuntime">Will determine if the routine call must be checked for visibility at runtime.</param> /// <param name="isCallMethod">Will determine if __call or __callStatic magic methods were found instead.</param> /// <returns>The resolved routine. Cannot return <c>null</c>.</returns> public DRoutine/*!*/ ResolveMethod(DType/*!*/ type, Name methodName, Position position, PhpType referringType, PhpRoutine referringRoutine, bool calledStatically, out bool checkVisibilityAtRuntime, out bool isCallMethod) { checkVisibilityAtRuntime = false; isCallMethod = false; // we cannot resolve a method unless we know the inherited members: if (type.IsDefinite) { KnownType known; // the method is a constructor: if (methodName.IsConstructName || (known = type as KnownType) != null && methodName.Equals(known.QualifiedName.Name)) return ResolveConstructor(type, position, referringType, referringRoutine, out checkVisibilityAtRuntime); DRoutine routine; GetMemberResult member_result; member_result = type.GetMethod(methodName, referringType, out routine); // Look for __call or __callStatic magic methods if no method was found: // Note: __call when looking for instance method is disabled, since there can be the searched method in some future override. if (member_result == GetMemberResult.NotFound && calledStatically) { // in PHP, it is possible to call instance methods statically if we are in instance method context. // In such case we have to look for __call instead of __callStatic: // determine the proper call method: // use __call for instance method invocation, including static method invocation within the current type (e.g. A::foo(), parent::foo(), ...) // use __callStatic for static method invocation Name callMethodName = (!calledStatically || // just to have complete condition here, always false (referringRoutine != null && referringType != null && !referringRoutine.IsStatic && // in non-static method type.TypeDesc.IsAssignableFrom(referringType.TypeDesc)) // {CurrentType} is inherited from or equal {type} ) ? DObject.SpecialMethodNames.Call : DObject.SpecialMethodNames.CallStatic; member_result = type.GetMethod(callMethodName, referringType, out routine); if (member_result != GetMemberResult.NotFound) isCallMethod = true; } switch (member_result) { case GetMemberResult.OK: return routine; case GetMemberResult.NotFound: if (calledStatically) // throw an error only in we are looking for static method, instance method can be defined in some future inherited class ErrorSink.Add(Errors.UnknownMethodCalled, SourceUnit, position, type.FullName, methodName); return new UnknownMethod(type, methodName.Value); case GetMemberResult.BadVisibility: { if (!calledStatically) // instance method will check the routine dynamically, there can be some override later return new UnknownMethod(type, methodName.Value); if (referringType == null && referringRoutine == null) { // visibility must be checked at run-time: checkVisibilityAtRuntime = true; return routine; } else { // definitive error: if (routine.IsPrivate) { ErrorSink.Add(Errors.PrivateMethodCalled, SourceUnit, position, type.FullName, methodName.Value, referringType.FullName); } else { ErrorSink.Add(Errors.ProtectedMethodCalled, SourceUnit, position, type.FullName, methodName.Value, referringType.FullName); } return new UnknownMethod(type, methodName.Value); } } default: Debug.Fail(); return null; } } else { // warning (if any) reported by the type resolver: return new UnknownMethod(type, methodName.Value); } }