/// <summary> /// Sets user-level session storage functions /// </summary> public static bool session_set_save_handler( Context ctx, IPhpCallable open, IPhpCallable close, IPhpCallable read, IPhpCallable write, IPhpCallable destroy, IPhpCallable gc, IPhpCallable create_sid = null, IPhpCallable validate_sid = null, IPhpCallable update_timestamp = null) { if (!ctx.IsWebApplication || !PhpVariable.IsValidBoundCallback(ctx, open) || !PhpVariable.IsValidBoundCallback(ctx, close) || !PhpVariable.IsValidBoundCallback(ctx, read) || !PhpVariable.IsValidBoundCallback(ctx, write) || !PhpVariable.IsValidBoundCallback(ctx, destroy) || !PhpVariable.IsValidBoundCallback(ctx, gc)) { return(false); } session_set_save_handler( ctx, sessionhandler: new CustomSessionHandler(ctx, open, close, read, write, destroy, gc, create_sid: create_sid, validate_sid: validate_sid, update_timestamp: update_timestamp), register_shutdown: false); return(true); }
/// <summary> /// Increases the level of buffering, enables output buffering if disabled and assignes the filtering callback /// to the new level of buffering. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="filter">The filtering callback. Ignores invalid callbacks.</param> /// <param name="chunkSize">Not supported.</param> /// <param name="erase">Not supported.</param> /// <returns>Whether the filter is valid callback.</returns> public static bool ob_start(Context ctx, IPhpCallable filter = null, int chunkSize = 0, bool erase = true) { if (chunkSize != 0) { //PhpException.ArgumentValueNotSupported("chunkSize", "!= 0"); throw new NotSupportedException("chunkSize != 0"); } if (!erase) { //PhpException.ArgumentValueNotSupported("erase", erase); throw new NotSupportedException("erase == false"); } ctx.BufferedOutput.IncreaseLevel(); bool result = true; // skips filter setting if filter is not specified or valid: if (filter != null) // && (result = filter.Bind())) // TODO: PhpCallback.Bind -> Delegate, done by caller { ctx.BufferedOutput.SetFilter(filter); } ctx.IsOutputBuffered = true; return(result); }
public static PhpValue preg_replace_callback(Context ctx, PhpValue pattern, IPhpCallable callback, PhpValue subject, long limit, ref long count) { count = 0; // PHP's behaviour for undocumented limit range if (limit < -1) { limit = 0; } // var pattern_array = pattern.AsArray(); if (pattern_array == null) { // string pattern return(PregReplaceInternal(ctx, pattern.ToStringOrThrow(ctx), null, callback, subject, (int)limit, ref count)); } else { // array pattern } throw new NotImplementedException(); }
/// <summary> /// Sets user defined handler to handle exceptions. /// </summary> /// <param name="ctx">Runtime context.</param> /// <param name="newHandler">The user callback called to handle an exceptions.</param> /// <returns> /// The PHP representation of previous user handler, <B>null</B> if there is no user one, or /// <B>false</B> if <paramref name="newHandler"/> is invalid or empty. /// </returns> /// <remarks> /// Stores old user handlers on the stack so that it is possible to /// go back to arbitrary previous user handler. /// </remarks> public static PhpValue set_exception_handler(Context ctx, IPhpCallable newHandler) { if (newHandler == null) { return(PhpValue.Null); } if (newHandler is PhpCallback && !((PhpCallback)newHandler).IsValid) { return(PhpValue.Null); } var errctx = GetErrorContext(ctx); //PhpCallback old_handler = Configuration.Local.ErrorControl.UserExceptionHandler; //// previous handler was defined by user => store it into the stack: //if (old_handler != null) //{ // if (OldUserExceptionHandlers == null) // { // OldUserExceptionHandlers = new Stack(5); // RequestContext.RequestEnd += new Action(ClearOldUserHandlers); // } // OldUserExceptionHandlers.Push(old_handler); //} //// sets the current handler: //Configuration.Local.ErrorControl.UserExceptionHandler = newHandler; //// returns the previous handler: //return (old_handler != null) ? old_handler.ToPhpRepresentation() : null; return(PhpValue.Null); }
/// <summary> /// Sets user defined handler to handle errors. /// </summary> /// <param name="ctx">Runtime context.</param> /// <param name="newHandler">The user callback called to handle an error.</param> /// <param name="errorTypes">Error types to be handled by the handler.</param> /// <returns> /// The PHP representation of previous user handler, <B>null</B> if there is no user one, or /// <B>false</B> if <paramref name="newHandler"/> is invalid or empty. /// </returns> /// <remarks> /// Stores old user handlers on the stack so that it is possible to /// go back to arbitrary previous user handler. /// </remarks> public static PhpValue set_error_handler(Context ctx, IPhpCallable newHandler, PhpErrorSets errorTypes = PhpErrorSets.Handleable) { if (newHandler == null) { return(PhpValue.Null); } if (newHandler is PhpCallback && !((PhpCallback)newHandler).IsValid) { return(PhpValue.Null); } var errctx = GetErrorContext(ctx); //var old_handler = ctx.UserErrorHandler; //var old_handlers = errctx.OldUserErrorHandlers; //// previous handler was defined by user => store it into the stack: //if (old_handler != null) //{ // if (old_handlers == null) // { // old_handlers = new Stack(5); // RequestContext.RequestEnd += new Action(ClearOldUserHandlers); // } // old_handlers.Push(new ErrorHandlerRecord(old_handler, old_errors)); //} //// sets the current handler: //Configuration.Local.ErrorControl.UserHandler = newHandler; //Configuration.Local.ErrorControl.UserHandlerErrors = (PhpError)errorTypes; //// returns the previous handler: //return (old_handler != null) ? old_handler.ToPhpRepresentation() : null; return(PhpValue.Null); }
/// <summary> /// Registers autoload function. /// </summary> public static bool spl_autoload_register(Context ctx, IPhpCallable autoloadFunction, bool throwError = true, bool prepend = false) { if (autoloadFunction == null) { //PhpException.ArgumentNull("autoloadFunction"); //return false; throw new ArgumentNullException(nameof(autoloadFunction)); } if (autoloadFunction is PhpCallback && !((PhpCallback)autoloadFunction).IsValid) { return(false); } var autoload = ctx.EnsureSplAutoload(); if (autoload.FindAutoload(autoloadFunction) != null) { return(false); } if (prepend) { autoload.Autoloaders.AddFirst(autoloadFunction); } else { autoload.Autoloaders.AddLast(autoloadFunction); } return(true); }
/// <summary> /// Sets user defined handler to handle errors. /// </summary> /// <param name="ctx">Runtime context.</param> /// <param name="newHandler">The user callback called to handle an error.</param> /// <param name="errorTypes">Error types to be handled by the handler.</param> /// <returns> /// The PHP representation of previous user handler, <B>null</B> if there is no user one, or /// <B>false</B> if <paramref name="newHandler"/> is invalid or empty. /// </returns> /// <remarks> /// Stores old user handlers on the stack so that it is possible to /// go back to arbitrary previous user handler. /// </remarks> public static PhpValue set_error_handler(Context ctx, IPhpCallable newHandler, PhpErrorSets errorTypes = PhpErrorSets.Handleable) { if (newHandler == null) { return(PhpValue.Null); } if (newHandler is PhpCallback && !((PhpCallback)newHandler).IsValid) { return(PhpValue.Null); } var config = ctx.Configuration.Core; var oldhandler = config.UserErrorHandler; var oldtypes = config.UserErrorTypes; if (oldhandler != null) { GetErrorContext(ctx).StoreErrorHandler(new ErrorHandlerRecord(oldhandler, oldtypes)); } config.UserErrorHandler = newHandler; config.UserErrorTypes = (PhpError)errorTypes; // returns the previous handler: return((oldhandler != null) ? oldhandler.ToPhpValue() : PhpValue.Null); }
/// <summary> /// Sets user defined handler to handle exceptions. /// </summary> /// <param name="ctx">Runtime context.</param> /// <param name="newHandler">The user callback called to handle an exceptions.</param> /// <returns> /// The PHP representation of previous user handler, <B>null</B> if there is no user one, or /// <B>false</B> if <paramref name="newHandler"/> is invalid or empty. /// </returns> /// <remarks> /// Stores old user handlers on the stack so that it is possible to /// go back to arbitrary previous user handler. /// </remarks> public static PhpValue set_exception_handler(Context ctx, IPhpCallable newHandler) { if (newHandler == null) { return(PhpValue.Null); } if (newHandler is PhpCallback callback && !callback.IsValid) { return(PhpValue.Null); } var config = ctx.Configuration.Core; var old_handler = config.UserExceptionHandler; config.UserExceptionHandler = newHandler; // previous handler was defined by user => store it into the stack: if (old_handler != null) { GetErrorContext(ctx).StoreExceptionHandler(new ErrorHandlerRecord(old_handler, (PhpError)PhpErrorSets.All)); // returns the previous handler: return(old_handler.ToPhpValue()); } else { return(PhpValue.Null); } }
/// <summary> /// Determines if given callable is valid and referes toi an existing function. /// </summary> public static bool IsValidBoundCallback(Context ctx, IPhpCallable callable) { var tmp = callable as PhpCallback; return((tmp != null) ? tmp.IsValidBound(ctx) : callable != null); }
/// <summary> /// Adds PHP handler to the event. /// </summary> public void addEventElapsed(Context ctx, IPhpCallable handler) { void HandlerDelegate(object sender, ElapsedEventArgs args) { handler.Invoke(ctx, PhpValue.FromClr(sender), PhpValue.FromClass(args)); } timer.Elapsed += new System.Timers.ElapsedEventHandler(HandlerDelegate); }
/// <summary> /// Assignes the filtering callback to the specified level of buffering. /// </summary> /// <param name="filter">The filter. Null reference means no filter.</param> /// <param name="levelIndex">The level of buffering which the filter to associate with.</param> /// <remarks>Data are filtered when flushed.</remarks> /// <exception cref="ArgumentOutOfRangeException"><paramref name="levelIndex"/> is out of range.</exception> public void SetFilter(IPhpCallable filter, int levelIndex) { if (levelIndex < 0 || levelIndex >= _levels.Count) { throw new ArgumentOutOfRangeException("levelIndex"); } _levels[levelIndex].filter = filter; }
/// <summary> /// Registers callback which will be called when script processing is complete but before the request /// has been complete. /// Function has no return value. /// </summary> /// <param name="ctx">Runtime context. Cannot be <c>null</c>.</param> /// <param name="function">The function which is called after main code of the script is finishes execution.</param> /// <param name="arguments">Parameters for the <paramref name="function"/>.</param> /// <remarks> /// Although, there is explicitly written in the PHP manual that it is not possible /// to send an output to a browser via echo or another output handling functions you can actually do so. /// </remarks> public static void register_shutdown_function(Context ctx, IPhpCallable function, params PhpValue[] arguments) { if (function == null) { PhpException.ArgumentNull(nameof(function)); return; } ctx.RegisterShutdownCallback((_ctx) => function.Invoke(_ctx, arguments)); }
/// <summary> /// Creates a new instance of a comparer using <see cref="PhpCallback"/> for comparisons. /// </summary> /// <param name="ctx">Current context. Cannot be <c>null</c>.</param> /// <param name="compare">User callback which provides comparing functionality.</param> /// <remarks> /// <para> /// Callback should have the signature <c>object(object,object)</c> and should already be bound. /// </para> /// <para> /// The result of calback's invocation is converted to a double by <see cref="PhpValue.ToDouble"/> /// and than the sign is taken as a result of the comparison.</para> /// </remarks> /// <exception cref="ArgumentNullException"><paramref name="compare"/> is a <B>null</B> reference.</exception> /// <exception cref="ArgumentException"><paramref name="compare"/> callback is not bound.</exception> public PhpUserComparer(Context ctx, IPhpCallable compare) { if (compare == null) { throw new ArgumentNullException(nameof(compare)); } _compare = compare; _ctx = ctx; }
/// <summary> /// Create and return a new anonymous function from given callable using the current scope. /// This method checks if the callable is callable in the current scope and throws a TypeError if it is not. /// </summary> public static Closure fromCallable(Context ctx, IPhpCallable callable) { if (callable is Closure closure) { return(closure); } if (callable != null && PhpVariable.IsValidBoundCallback(ctx, callable)) { return(new Closure(ctx, callable, null, default, null, PhpArray.Empty, PhpArray.Empty));
/// <summary> /// Gets or sets option. /// </summary> public static IPhpCallable GetSet(ref IPhpCallable option, IPhpCallable defaultValue, PhpValue newValue, IniAction action) { var oldValue = option; if (action == IniAction.Set) { option = newValue.AsCallable(default(RuntimeTypeHandle)); } return(oldValue); }
/// <summary> /// Calls a function or a method defined by callback with given arguments. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="function">Target callback.</param> /// <param name="args">The arguments.</param> /// <returns>The return value.</returns> public static PhpValue call_user_func(Context ctx, IPhpCallable function, params PhpValue[] args) { if (function == null) { PhpException.ArgumentNull("function"); return(PhpValue.Null); } Debug.Assert(args != null); // invoke the callback: return(function.Invoke(ctx, args)); }
/// <summary> /// Registers a function that will be called when PHP starts sending output. /// </summary> public static bool header_register_callback(Context ctx, IPhpCallable callback) { var webctx = ctx.HttpPhpContext; if (webctx == null || callback == null) { return(false); } webctx.HeadersSending += () => { callback.Invoke(ctx); }; return(true); }
/// <summary> /// Gets some information about a specified level. /// </summary> /// <param name="levelIndex">Level index starting from 1.</param> /// <param name="filter">Filtering callback (if any).</param> /// <param name="size">Data size.</param> /// <param name="name">Optionally the level name.</param> public void GetLevelInfo(int levelIndex, out IPhpCallable filter, out int size, out string name) { if (levelIndex < 1 || levelIndex > Level) { throw new ArgumentOutOfRangeException("levelIndex"); } var element = _levels[levelIndex - 1]; filter = element.filter; size = element.size; name = GetLevelName(element); }
/// <summary> /// Calls a function or a method defined by callback with given arguments. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="function">Target callback.</param> /// <param name="args">The arguments.</param> /// <returns>The return value.</returns> public static PhpValue call_user_func(Context ctx, IPhpCallable function, params PhpValue[] args) { if (function == null) { //PhpException.ArgumentNull("function"); //return null; throw new ArgumentNullException(); // NOTE: should not be reached, runtime converts NULL to InvalidCallback instance } Debug.Assert(args != null); // invoke the callback: return function.Invoke(ctx, args); }
/// <summary> /// Create and return a new anonymous function from given callable using the current scope. /// This method checks if the callable is callable in the current scope and throws a TypeError if it is not. /// </summary> public static Closure fromCallable(Context ctx, IPhpCallable callable) { if (callable == null) { throw new ArgumentNullException(nameof(callable)); } if (callable is Closure) { return((Closure)callable); } // return(new Closure(ctx, callable, null, default, null, PhpArray.Empty, PhpArray.Empty));
/// <summary> /// Unregisteres the autoload function. /// </summary> public static bool spl_autoload_unregister(Context ctx, IPhpCallable autoloadFunction) { var functionNode = FindAutoloadFunction(ctx, autoloadFunction); if (functionNode != null) { functionNode.List.Remove(functionNode); return(true); } else { return(false); } }
/// <summary> /// Find level index by the filter callback. /// </summary> /// <param name="filter"></param> /// <returns></returns> public int FindLevelByFilter(IPhpCallable filter) { if (_levels != null && filter != null) { for (int i = 0; i < Level; i++) { if (_levels[i].filter == filter) { return(i); } } } return(-1); }
/// <summary> /// Constructs the closure. /// </summary> internal Closure(Context /*!*/ ctx, IPhpCallable /*!*/ routine, object @this, RuntimeTypeHandle scope, PhpArray /*!*/ parameter, PhpArray /*!*/ @static) { Debug.Assert(ctx != null); Debug.Assert(routine != null); Debug.Assert(parameter != null); Debug.Assert(@static != null); _callable = routine; _ctx = ctx; _this = @this; _scope = scope; this.parameter = parameter; this.@static = @static; }
/// <summary> /// Finds node within <see cref="_autoloaders"/> matches given callback. /// </summary> public LinkedListNode <IPhpCallable> FindAutoload(IPhpCallable autoloadFunction) { if (autoloadFunction != null) { for (var node = _autoloaders.First; node != null; node = node.Next) { if (object.ReferenceEquals(node.Value, autoloadFunction) || node.Value.Equals(autoloadFunction)) { return(node); } } } return(null); }
/// <summary> /// Calls a function or a method defined by callback with arguments stored in an array. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="function">Target callback.</param> /// <param name="args">Arguments. Can be null.</param> /// <returns>The returned value.</returns> public static PhpValue call_user_func_array(Context ctx, IPhpCallable function, PhpArray args) { PhpValue[] args_array; if (args != null && args.Count != 0) { args_array = new PhpValue[args.Count]; args.CopyValuesTo(args_array, 0); } else { args_array = Core.Utilities.ArrayUtils.EmptyValues; } return call_user_func(ctx, function, args_array); }
/// <summary> /// Calls a function or a method defined by callback with arguments stored in an array. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="function">Target callback.</param> /// <param name="args">Arguments. Can be null.</param> /// <returns>The returned value.</returns> public static PhpValue call_user_func_array(Context ctx, IPhpCallable function, PhpArray args) { PhpValue[] args_array; if (args != null && args.Count != 0) { args_array = new PhpValue[args.Count]; args.CopyValuesTo(args_array, 0); } else { args_array = Core.Utilities.ArrayUtils.EmptyValues; } return(call_user_func(ctx, function, args_array)); }
/// <summary> /// Calls a function or a method defined by callback with given arguments. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="function">Target callback.</param> /// <param name="args">The arguments.</param> /// <returns>The return value.</returns> public static PhpValue call_user_func(Context ctx, IPhpCallable function, params PhpValue[] args) { if (function == null) { PhpException.ArgumentNull("function"); return(PhpValue.Null); } else if (!PhpVariable.IsValidBoundCallback(ctx, function)) { PhpException.InvalidArgument(nameof(function)); return(PhpValue.Null); } Debug.Assert(args != null); // invoke the callback: return(function.Invoke(ctx, args)); }
public CustomSessionHandler( Context ctx, IPhpCallable open, IPhpCallable close, IPhpCallable read, IPhpCallable write, IPhpCallable destroy, IPhpCallable gc, IPhpCallable create_sid = null, IPhpCallable validate_sid = null, IPhpCallable update_timestamp = null) { _ctx = ctx; _open = open; _close = close; _read = read; _write = write; _destroy = destroy; _gc = gc; _create_sid = create_sid; _validate_sid = validate_sid; _update_timestamp = update_timestamp; }
/// <summary> /// Calls a function for every element in an iterator. /// </summary> /// <param name="ctx">Runtime context.</param> /// <param name="iterator">The class to iterate over.</param> /// <param name="function">The callback function to call on every element. /// Note: The function must return <c>TRUE</c> in order to continue iterating over the iterator.</param> /// <param name="args">Arguments to pass to the callback function.</param> /// <returns>The iteration count.</returns> public static int iterator_apply(Context ctx, Iterator iterator, IPhpCallable function, PhpArray args = null) { if (iterator == null) { throw new ArgumentNullException(nameof(iterator)); } if (function == null) { PhpException.ArgumentNull(nameof(function)); return(-1); } // construct callback arguments var args_array = args != null ? args.GetValues() : Array.Empty <PhpValue>(); // int n = 0; iterator.rewind(); while (iterator.valid()) { if (function.Invoke(ctx, args_array).ToBoolean() == false) { break; } // n++; iterator.next(); } // return(n); }
public static TFunc Get(IPhpCallable callable, Context ctx) { if (callable == null) { throw new ArgumentNullException(nameof(callable)); } // if (typeof(TFunc) == typeof(Action)) { return((TFunc)(object)new Action(() => callable.Invoke(ctx))); } if (typeof(TFunc) == typeof(Func <bool>)) { return((TFunc)(object)new Func <bool>(() => (bool)callable.Invoke(ctx))); } if (typeof(TFunc) == typeof(Func <long, long>)) { return((TFunc)(object)new Func <long, long>((p1) => (long)callable.Invoke(ctx, p1))); } // throw new NotImplementedException($"Creating delegate of type '{typeof(TFunc)}'."); // TODO: construct the delegate dynamically }
public static TFunc Get(IPhpCallable callable, Context ctx) { if (callable == null) { throw new ArgumentNullException(nameof(callable)); } // if (typeof(TFunc) == typeof(Action)) { return((TFunc)(object)new Action(() => callable.Invoke(ctx))); } if (typeof(TFunc) == typeof(Func <bool>)) { return((TFunc)(object)new Func <bool>(() => (bool)callable.Invoke(ctx))); } if (typeof(TFunc) == typeof(Func <long, long>)) { return((TFunc)(object)new Func <long, long>((p1) => (long)callable.Invoke(ctx, p1))); } // return(Dynamic.BinderHelpers.CreateDelegate <TFunc>(callable, ctx)); }
/// <summary> /// Sorts an array using user comparison callback for comparing values. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="array">The array to be sorted.</param> /// <param name="compare">The user callback to be used for comparison of values.</param> /// <remarks>Resets <paramref name="array"/>'s intrinsic enumerator.</remarks> /// <returns>True on success, False on failure.</returns> public static bool usort(Context ctx /*, caller*/, [In, Out] PhpArray array, IPhpCallable compare) { if (array == null) { //PhpException.ReferenceNull("array"); //return false; throw new ArgumentNullException(); } if (!PhpVariable.IsValidCallback(compare)) return false; // sorts array using callback for comparisons: array.Sort(new ValueComparer(new PhpUserComparer(ctx, compare), false)); array.ReindexAll(); array.RestartIntrinsicEnumerator(); return true; }
/// <summary> /// Sorts an array using user comparison callback for comparing keys. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="array">The array to be sorted.</param> /// <param name="compare">The user callback to be used for comparison of values.</param> /// <remarks>Resets <paramref name="array"/>'s intrinsic enumerator.</remarks> /// <returns>True on success, False on failure.</returns> public static bool uksort(Context ctx /*, caller*/, [In, Out] PhpArray array, IPhpCallable compare) { if (array == null) { //PhpException.ReferenceNull("array"); //return false; throw new ArgumentNullException(); } if (!PhpVariable.IsValidCallback(compare)) return false; array.Sort(new KeyComparer(new PhpUserComparer(ctx, compare), false)); return true; }
/// <summary> /// Applies a callback function on specified tuples one by one storing its results to an array. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="map"> /// A callback to be called on tuples. The number of arguments should be the same as /// the number of arrays specified by <pramref name="arrays"/>. /// Arguments passed by reference modifies elements of <pramref name="arrays"/>. /// A <B>null</B> means default callback which makes integer indexed arrays from the tuples is used. /// </param> /// <param name="arrays">Arrays where to load tuples from. </param> /// <returns>An array of return values of the callback /// keyed by keys of the <paramref name="arrays"/> if it /// is a single array or by integer keys starting from 0.</returns> /// <remarks> /// <para> /// In the <I>i</I>-th call the <I>j</I>-th parameter of the callback will be /// the <I>i</I>-th value of the <I>j</I>-the array or a <B>null</B> if that array /// has less then <I>i</I> entries. /// </para> /// <para> /// If the callback assigns a value to a parameter passed by reference in the <I>i</I>-the call /// and the respective array contains at least <I>i</I> elements the assigned value is propagated /// to the array. /// </para> /// </remarks> public static PhpArray array_map(Context ctx /*, caller*/, IPhpCallable map, [In, Out] params PhpArray[] arrays) { //if (!PhpArgument.CheckCallback(map, caller, "map", 0, true)) return null; if (arrays == null || arrays.Length == 0) { //PhpException.InvalidArgument("arrays", LibResources.GetString("arg:null_or_emtpy")); //return null; throw new ArgumentException(); } // if callback has not been specified uses the default one: if (map == null) { map = _mapIdentity; } int count = arrays.Length; bool preserve_keys = count == 1; var args = new PhpValue[count]; var iterators = new OrderedDictionary.FastEnumerator[count]; PhpArray result; // initializes iterators and args array, computes length of the longest array: int max_count = 0; for (int i = 0; i < arrays.Length; i++) { var array = arrays[i]; if (array == null) { //PhpException.Throw(PhpError.Warning, LibResources.GetString("argument_not_array", i + 2));// +2 (first arg is callback) //return null; throw new ArgumentException(); } args[i] = PhpValue.CreateAlias(); iterators[i] = array.GetFastEnumerator(); if (array.Count > max_count) max_count = array.Count; } // keys are preserved in a case of a single array and re-indexed otherwise: if (preserve_keys) result = new PhpArray(arrays[0].IntegerCount, arrays[0].StringCount); else result = new PhpArray(max_count, 0); for (;;) { bool hasvalid = false; // fills args[] with items from arrays: for (int i = 0; i < arrays.Length; i++) { if (iterators[i].IsValid) { hasvalid = true; // note: deep copy is not necessary since a function copies its arguments if needed: args[i].Alias.Value = iterators[i].CurrentValue.GetValue(); // TODO: throws if the current Value is PhpReference } else { args[i].Alias.Value = PhpValue.Null; } } if (!hasvalid) break; // invokes callback: var return_value = map.Invoke(ctx, args); // return value is not deeply copied: if (preserve_keys) result.Add(iterators[0].CurrentKey, return_value); else result.Add(return_value); // loads new values (callback may modify some by ref arguments): for (int i = 0; i < arrays.Length; i++) { if (iterators[i].IsValid) { var item = iterators[i].CurrentValue; if (item.IsAlias) { item.Alias.Value = args[i].Alias.Value; } else { iterators[i].CurrentValue = args[i].Alias.Value; } // iterators[i].MoveNext(); } } } return result; }
/// <summary> /// Visits an entry of array which <see cref="array_walk(Context, PhpHashtable, IPhpCallable, PhpValue)"/> or <see cref="array_walk_recursive(Context, PhpHashtable, IPhpCallable, PhpValue)"/> is walking through. /// </summary> private static void VisitEntryOnWalk(Context ctx, KeyValuePair<IntStringKey, PhpValue> entry, IDictionary<IntStringKey, PhpValue> array, IPhpCallable callback, PhpValue[] args) { Debug.Assert(args[0].IsAlias); // fills arguments for the callback: args[0].Alias.Value = entry.Value.GetValue(); args[1] = PhpValue.Create(entry.Key); // invoke callback: callback.Invoke(ctx, args); // loads a new value from a reference: if (entry.Value.IsAlias) { entry.Value.Alias.Value = args[0].Alias.Value; } else { array[entry.Key] = args[0].Alias.Value; } }
public static PhpValue array_reduce(Context ctx, [In, Out] PhpArray array, IPhpCallable function, PhpValue initialValue) { if (array == null) { //PhpException.ReferenceNull("array"); //return PhpValue.Null; throw new ArgumentNullException(nameof(array)); } //if (!PhpArgument.CheckCallback(function, caller, "function", 0, false)) return null; if (array.Count == 0) { return initialValue; } PhpValue[] args = new PhpValue[] { initialValue.DeepCopy(), PhpValue.Null }; var iterator = array.GetFastEnumerator(); while (iterator.MoveNext()) { var item = iterator.CurrentValue; args[1] = item.IsAlias ? item : PhpValue.Create(item.EnsureAlias()); args[0] = function.Invoke(ctx, args); // updates an item if it wasn't alias if (!item.IsAlias) { iterator.CurrentValue = args[1].Alias.Value; } } // dereferences the last returned value: return args[0].GetValue(); }
/// <summary> /// Applies a user function or method on each element of a specified array or dictionary. /// </summary> /// <returns><B>true</B>.</returns> /// <remarks>See <see cref="Walk(PHP.Core.Reflection.DTypeDesc,PhpHashtable,PhpCallback,object)"/> for details.</remarks> /// <exception cref="PhpException"><paramref name="function"/> or <paramref name="array"/> are <B>null</B> references.</exception> public static bool array_walk(Context ctx, [In, Out] PhpHashtable array, IPhpCallable function) { return array_walk(ctx, array, function, PhpValue.Null); }
/// <summary> /// Applies a user function or method on each element (value) of a specified dictionary. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="array">The array (or generic dictionary) to walk through.</param> /// <param name="callback"> /// The callback called for each element of <paramref name="array"/>. /// The callback is assumed to have two or three parameters: /// <list type="number"> /// <item> /// <term> /// A value of dictionary entry. Can be specified with & modifier which propagates any changes /// make to the argument back to the entry. The dictionary can be changed in this way. /// </term> /// </item> /// <item>A key of dictionary entry.</item> /// <item> /// Value of <paramref name="data"/> parameter if it is not a <B>null</B> reference. /// Otherwise, the callback is assumed to have two parameters only. /// </item> /// </list> /// </param> /// <param name="data">An additional parameter passed to <paramref name="callback"/> as its third parameter.</param> /// <returns><B>true</B>.</returns> /// <exception cref="PhpException"><paramref name="callback"/> or <paramref name="array"/> are <B>null</B> references.</exception> public static bool array_walk(Context ctx /*, caller*/, [In, Out] PhpHashtable array, IPhpCallable callback, PhpValue data) { PhpValue[] args = PrepareWalk(array, callback, data); if (args == null) return false; var iterator = array.GetFastEnumerator(); while (iterator.MoveNext()) { VisitEntryOnWalk(ctx, iterator.Current, array, callback, args); } return true; }
/// <summary> /// Applies a user function or method on each element (value) of a specified dictionary recursively. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="array">The array to walk through.</param> /// <param name="callback">The callback called for each element of <paramref name="array"/>.</param> /// <param name="data">An additional parameter passed to <paramref name="callback"/> as its third parameter.</param> /// <exception cref="PhpException"><paramref name="callback"/> or <paramref name="array"/> are <B>null</B> references.</exception> /// <remarks><seealso cref="Walk"/>.</remarks> public static bool array_walk_recursive(Context ctx /*, caller*/, [In, Out] PhpHashtable array, IPhpCallable callback, PhpValue data) { var args = PrepareWalk(array, callback, data); if (args == null) { return false; } using (var iterator = array.GetRecursiveEnumerator(true, false)) { while (iterator.MoveNext()) { var current = iterator.Current; // visits the item unless it is an array or a reference to an array: if (!current.Value.GetValue().IsArray) { VisitEntryOnWalk(ctx, iterator.Current, iterator.CurrentTable, callback, args); } } } return true; }
public static PhpValue array_reduce(Context ctx, [In, Out] PhpArray array, IPhpCallable function) { return array_reduce(ctx, array, function, PhpValue.Null); }
/// <summary> /// Filters an array using a specified callback. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="array">The array to be filtered.</param> /// <param name="callback"> /// The callback called on each value in the <paramref name="array"/>. /// If the callback returns value convertible to <B>true</B> the value is copied to the resulting array. /// Otherwise, it is ignored. /// </param> /// <returns>An array of unfiltered items.</returns> //[return: PhpDeepCopy] public static PhpArray array_filter(Context ctx /*, caller*/, PhpArray array, IPhpCallable callback) { if (array == null) { //PhpException.ArgumentNull("array"); //return null; throw new ArgumentNullException(nameof(array)); } if (callback == null) { //PhpException.ArgumentNull("callback"); //return null; throw new ArgumentNullException(nameof(callback)); } var result = new PhpArray(); var args = new PhpValue[1]; var iterator = array.GetFastEnumerator(); while (iterator.MoveNext()) { var entry = iterator.Current; // no deep copying needed because it is done so in callback: args[0] = entry.Value; // adds entry to the resulting array if callback returns true: if (callback.Invoke(ctx, args).ToBoolean()) { result.Add(entry); } } // values should be inplace deeply copied: //result.InplaceCopyOnReturn = true; return result; }
public static PhpCallback Create(IPhpCallable callable) => Create(callable.Invoke);
// TODO: specific parameters instead of 'params PhpValue[] arraysAndComparer' /// <summary> /// There have to be at least 1 value in <paramref name="vars"/>. /// The last is converted to callback, the rest to arrays. /// </summary> private static bool SplitArraysAndComparers(int comparerCount, PhpArray array, PhpValue[] vars, out PhpArray[] arrays, out IPhpCallable cmp1, out IPhpCallable cmp2) { arrays = null; cmp1 = cmp2 = null; if (vars == null || vars.Length == 0) { // TODO: PhpException.InvalidArgumentCount(null, null); return false; } // the first callback: cmp1 = vars[vars.Length - comparerCount].AsCallable(); if (PhpVariable.IsValidCallback(cmp1)) return false; // the second callback: if (comparerCount > 1) { cmp2 = vars[vars.Length - 1].AsCallable(); if (!PhpVariable.IsValidCallback(cmp2)) return false; } // remaining arguments should be arrays: arrays = new PhpArray[vars.Length - comparerCount + 1]; arrays[0] = array; for (int i = 0; i < vars.Length - comparerCount; i++) { var var = vars[i]; if ((arrays[i + 1] = vars[i].AsArray()) == null) { // TODO: PhpException.Throw(PhpError.Warning, LibResources.GetString("argument_not_array", i + 3)); return false; } } // return true; }
/// <summary> /// Prepares a walk for <see cref="array_walk(Context, PhpHashtable, IPhpCallable, PhpValue)"/> and <see cref="array_walk_recursive(Context, PhpHashtable, IPhpCallable, PhpValue)"/> methods. /// </summary> /// <exception cref="PhpException"><paramref name="callback"/> or <paramref name="array"/> are <B>null</B> references.</exception> private static PhpValue[] PrepareWalk(IDictionary array, IPhpCallable callback, PhpValue data) { if (callback == null) { //PhpException.ArgumentNull("callback"); // return null; throw new ArgumentNullException(nameof(callback)); } if (array == null) { //PhpException.ArgumentNull("array"); //return null; throw new ArgumentNullException(nameof(array)); } // prepares an array of callback's arguments (no deep copying needed because it is done so in callback): return (data != null) ? new PhpValue[] { PhpValue.CreateAlias(), PhpValue.Null, data } : new PhpValue[] { PhpValue.CreateAlias(), PhpValue.Null }; }
/// <summary> /// Applies a user function or method on each element of a specified array recursively. /// </summary> /// <returns><B>true</B>.</returns> /// <remarks>See <see cref="Walk(PHP.Core.Reflection.DTypeDesc,PhpHashtable,PhpCallback,object)"/> for details.</remarks> /// <exception cref="PhpException"><paramref name="callback"/> or <paramref name="array"/> are <B>null</B> references.</exception> public static bool array_walk_recursive(Context ctx, [In, Out] PhpHashtable array, IPhpCallable callback) { return array_walk_recursive(ctx, array, callback, PhpValue.Null); }