public override object VisitGlobalFunctionCall(BoundGlobalFunctionCall x) { // TODO: extensible, dictionary of functions ? if (x.Name.NameValue == NameUtils.SpecialNames.dirname) { // dirname( __FILE__ ) -> __DIR__ if (x.ArgumentsInSourceOrder.Length == 1 && x.ArgumentsInSourceOrder[0].Value is BoundPseudoConst pc && pc.ConstType == Ast.PseudoConstUse.Types.File) { TransformationCount++; return(new BoundPseudoConst(Ast.PseudoConstUse.Types.Dir).WithAccess(x.Access)); } } else if (x.Name.NameValue == NameUtils.SpecialNames.basename) { // basename( __FILE__ ) -> "filename" if (x.ArgumentsInSourceOrder.Length == 1 && x.ArgumentsInSourceOrder[0].Value is BoundPseudoConst pc && pc.ConstType == Ast.PseudoConstUse.Types.File) { TransformationCount++; var fname = _routine.ContainingFile.FileName; return(new BoundLiteral(fname) { ConstantValue = new Optional <object>(fname) }.WithContext(x)); } } else if (x.Name.NameValue == NameUtils.SpecialNames.get_parent_class) { bool TryResolveParentClassInCurrentClassContext(SourceRoutineSymbol routine, out BoundLiteral newExpression) { // in global function, always FALSE if (routine is SourceFunctionSymbol) { // FALSE newExpression = new BoundLiteral(false.AsObject()); return(true); } // in a method, we can resolve in compile time: if (routine is SourceMethodSymbol m && m.ContainingType is SourceTypeSymbol t && !t.IsTrait) { if (t.BaseType == null || t.BaseType.IsObjectType()) { // FALSE newExpression = new BoundLiteral(false.AsObject()) { ConstantValue = false.AsOptional() }; return(true); } else { // {class name} var baseTypeName = t.BaseType.PhpQualifiedName().ToString(); newExpression = new BoundLiteral(baseTypeName) { ConstantValue = baseTypeName }; return(true); } } // newExpression = default; return(false); } // get_parent_class() -> {class name} | FALSE if (x.ArgumentsInSourceOrder.Length == 0) { if (TryResolveParentClassInCurrentClassContext(_routine, out var newExpression)) { TransformationCount++; return(newExpression.WithContext(x)); } } // get_parent_class( ??? ) -> parent::class | FALSE if (x.ArgumentsInSourceOrder.Length == 1) { // get_parent_class($this), get_parent_class(__CLASS__) -> {class name} | FALSE if ((x.ArgumentsInSourceOrder[0].Value is BoundVariableRef varref && varref.Variable is ThisVariableReference) || (x.ArgumentsInSourceOrder[0].Value is BoundPseudoConst pc && pc.ConstType == Ast.PseudoConstUse.Types.Class)) { if (TryResolveParentClassInCurrentClassContext(_routine, out var newExpression)) { TransformationCount++; return(newExpression.WithContext(x)); } } } } else if ( // method_exists(class_name, method_name) -> FALSE x.Name.NameValue == NameUtils.SpecialNames.method_exists && x.ArgumentsInSourceOrder.Length == 2) { // method_exists(FALSE, ...) -> FALSE var value = x.ArgumentsInSourceOrder[0].Value.ConstantValue; if (value.HasValue && value.TryConvertToBool(out var bvalue) && !bvalue) { TransformationCount++; return(new BoundLiteral(false.AsObject()) { ConstantValue = false.AsOptional() }.WithContext(x)); } } else if ( // ini_get( {svalue} ) : string|FALSE x.Name.NameValue == NameUtils.SpecialNames.ini_get && x.ArgumentsInSourceOrder.Length == 1 && x.ArgumentsInSourceOrder[0].Value.ConstantValue.TryConvertToString(out var svalue)) { // options we're not supporting for sure // always FALSE if (svalue.StartsWith("xdebug.") || svalue.StartsWith("xcache.") || svalue.StartsWith("opcache.") || svalue.StartsWith("apc.")) { TransformationCount++; return(new BoundLiteral(false.AsObject()) { ConstantValue = false.AsOptional() }.WithContext(x)); // TODO: well-known ini options can be translated to access the configuration property directly } } else if ( // extension_loaded( {ext_name} ) : bool x.Name.NameValue == NameUtils.SpecialNames.extension_loaded && x.ArgumentsInSourceOrder.Length == 1 && x.ArgumentsInSourceOrder[0].Value.ConstantValue.TryConvertToString(out var ext_name)) { TransformationCount++; bool hasextension = DeclaringCompilation .GlobalSemantics .Extensions .Contains(ext_name, StringComparer.OrdinalIgnoreCase); // CONSIDER: only when hasextension == True ? Can we add extensions in runtime ? Trace.WriteLine($"'extension_loaded({ext_name})' evaluated to {hasextension}."); return(new BoundLiteral(hasextension.AsObject()) { ConstantValue = hasextension.AsOptional() }.WithContext(x)); } // return(base.VisitGlobalFunctionCall(x)); }