<commit_before>﻿using Pchp.Core;
using Pchp.Core.Reflection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Pchp.Library.Resources;
using Pchp.Library.Reflection;
using System.Diagnostics;

namespace Pchp.Library
{
    public static class Objects
    {
        /// <summary>
		/// Tests whether the class given by <paramref name="classNameOrObject"/> is derived from a class given by <paramref name="baseClassName"/>.
		/// </summary>
        /// <param name="ctx">Runtime context.</param>
        /// <param name="classNameOrObject">The object or the name of a class (<see cref="string"/>).</param>
		/// <param name="baseClassName">The name of a base class or interface.</param>
        /// <param name="allow_string">Whether class name can be used as <paramref name="classNameOrObject"/>. Otherwise only an object instance is allowed.
        /// This can be used to prevent from calling autoloader if the class doesn't exist.</param>
		/// <returns><B>true</B> if <paramref name="classNameOrObject"/> implements or extends <paramref name="baseClassName"/>, <B>false</B> otherwise.</returns>
		public static bool is_subclass_of(Context ctx, PhpValue classNameOrObject, string baseClassName, bool allow_string = true)
        {
            var tinfo = TypeNameOrObjectToType(ctx, classNameOrObject, allowName: allow_string);
            if (tinfo == null) return false;

            // look for the class, do not use autoload (since PHP 5.1):
            var base_tinfo = ctx.GetDeclaredType(baseClassName, false);
            if (base_tinfo == null) return false;

            //
            return
                base_tinfo != tinfo &&
                base_tinfo.Type.IsAssignableFrom(tinfo.Type);
        }

        /// <summary>
		/// Tests whether a given class is defined.
		/// </summary>
        /// <param name="ctx">Current runtime context.</param>
        /// <param name="className">The name of the class.</param>
		/// <param name="autoload">Whether to attempt to call <c>__autoload</c>.</param>
		/// <returns><B>true</B> if the class given by <paramref name="className"/> has been defined,
		/// <B>false</B> otherwise.</returns>
		public static bool class_exists(Context ctx, string className, bool autoload = true)
        {
            var info = ctx.GetDeclaredType(className, autoload);
            return info != null && !info.IsInterface;
        }

        /// <summary>
		/// Tests whether a given interface is defined.
		/// </summary>
        /// <param name="ctx">Current runtime context.</param>
        /// <param name="ifaceName">The name of the interface.</param>
		/// <param name="autoload">Whether to attempt to call <c>__autoload</c>.</param>
		/// <returns><B>true</B> if the interface given by <paramref name="ifaceName"/> has been defined,
		/// <B>false</B> otherwise.</returns>
		public static bool interface_exists(Context ctx, string ifaceName, bool autoload = true)
        {
            var info = ctx.GetDeclaredType(ifaceName, autoload);
            return info != null && info.IsInterface;
        }

        /// <summary>
		/// Tests whether a given trait is defined.
		/// </summary>
        /// <param name="ctx">Current runtime context.</param>
        /// <param name="traitname">The name of the trait.</param>
		/// <param name="autoload">Whether to attempt to call <c>__autoload</c>.</param>
		/// <returns><B>true</B> if the trait given by <paramref name="traitname"/> has been defined,
		/// <B>false</B> otherwise.</returns>
		public static bool trait_exists(Context ctx, string traitname, bool autoload = true)
        {
            var info = ctx.GetDeclaredType(traitname, autoload);
            return info != null && info.IsTrait;
        }

        /// <summary>
        /// Returns the name of the callers class context.
        /// </summary>
        /// <param name="tctx">Current class context.</param>
        /// <returns>Current class name.</returns>
        [return: CastToFalse]
        public static string get_class([ImportCallerClass]string tctx)
        {
            return tctx;
        }

        /// <summary>
        /// Returns the name of the class of which the object <paramref name="obj"/> is an instance.
        /// </summary>
        /// <param name="tctx">Current class context.</param>
        /// <param name="obj">The object whose class is requested.</param>
        /// <returns><paramref name="obj"/>'s class name or current class name if <paramref name="obj"/> is <B>null</B>.</returns>
        [return: CastToFalse]
        public static string get_class([ImportCallerClass]string tctx, PhpValue obj)
        {
            if (obj.IsSet)
            {
                if (obj.IsObject)
                {
                    return obj.Object.GetPhpTypeInfo().Name;
                }
                else if (obj.IsAlias)
                {
                    return get_class(tctx, obj.Alias.Value);
                }
                else
                {
                    // TODO: E_WARNING
                    throw new ArgumentException(nameof(obj));
                }
            }

            return tctx;
        }

        /// <summary>
        /// Gets the name of the class the static method is called in.
        /// </summary>
        [return: CastToFalse]
        public static string get_called_class([ImportCallerStaticClass]PhpTypeInfo @static) => @static?.Name;

        /// <summary>
        /// Helper getting declared classes or interfaces.
        /// </summary>
        /// <param name="ctx">Runtime context with declared types.</param>
        /// <param name="interfaces">Whether to list interfaces or classes.</param>
        /// <param name="traits">Whether to list traits.</param>
        static PhpArray get_declared_types(Context ctx, bool interfaces, bool traits)
        {
            var result = new PhpArray();

            foreach (var t in ctx.GetDeclaredTypes())
            {
                if (t.IsInterface == interfaces && t.IsTrait == traits)
                {
                    result.Add(t.Name);
                }
            }

            return result;
        }

        /// <summary>
		/// Returns a <see cref="PhpArray"/> with names of all defined classes (system and user).
		/// </summary>
		/// <returns><see cref="PhpArray"/> of class names.</returns>
		public static PhpArray get_declared_classes(Context ctx) => get_declared_types(ctx, false, false);

        /// <summary>
        /// Returns a <see cref="PhpArray"/> with names of all defined interfaces (system and user).
        /// </summary>
        /// <returns><see cref="PhpArray"/> of interface names.</returns>
        public static PhpArray get_declared_interfaces(Context ctx) => get_declared_types(ctx, true, false);

        /// <summary>
        /// Returns a <see cref="PhpArray"/> with names of all defined traits (system and user).
        /// </summary>
        /// <returns><see cref="PhpArray"/> of traits names.</returns>
        public static PhpArray get_declared_traits(Context ctx) => get_declared_types(ctx, false, true);

        /// <summary>
		/// Gets the name of the class from which class given by <paramref name="classNameOrObject"/>
		/// inherits.
		/// </summary>
        /// <remarks>
		/// If the class given by <paramref name="classNameOrObject"/> has no parent in PHP class hierarchy, this method returns <B>false</B>.
		/// </remarks>
		[return: CastToFalse]
        public static string get_parent_class(Context ctx, [ImportCallerClass]RuntimeTypeHandle caller, PhpValue classNameOrObject)
        {
            var tinfo = TypeNameOrObjectToType(ctx, classNameOrObject, caller);

            //
            var btype = tinfo?.BaseType;
            return btype?.Name;
        }

        /// <summary>
		/// Tests whether <paramref name="obj"/>'s class is derived from a class given by <paramref name="class_name"/>.
		/// </summary>
        /// <param name="ctx">Runtime context.</param>
        /// <param name="obj">The object to test.</param>
		/// <param name="class_name">The name of the class.</param>
        /// <returns><B>true</B> if the object <paramref name="obj"/> belongs to <paramref name="class_name"/> class or
		/// a class which is a subclass of <paramref name="class_name"/>, <B>false</B> otherwise.</returns>
        public static bool is_a(Context ctx, object obj, string class_name)
        {
            return obj != null && Core.Convert.IsInstanceOf(obj, ctx.GetDeclaredType(class_name));  // double check (obj!=null) for performance reasons<commit_msg>get_parent_class()<commit_after>﻿using Pchp.Core;
using Pchp.Core.Reflection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Pchp.Library.Resources;
using Pchp.Library.Reflection;
using System.Diagnostics;

namespace Pchp.Library
{
    public static class Objects
    {
        /// <summary>
		/// Tests whether the class given by <paramref name="classNameOrObject"/> is derived from a class given by <paramref name="baseClassName"/>.
		/// </summary>
        /// <param name="ctx">Runtime context.</param>
        /// <param name="classNameOrObject">The object or the name of a class (<see cref="string"/>).</param>
		/// <param name="baseClassName">The name of a base class or interface.</param>
        /// <param name="allow_string">Whether class name can be used as <paramref name="classNameOrObject"/>. Otherwise only an object instance is allowed.
        /// This can be used to prevent from calling autoloader if the class doesn't exist.</param>
		/// <returns><B>true</B> if <paramref name="classNameOrObject"/> implements or extends <paramref name="baseClassName"/>, <B>false</B> otherwise.</returns>
		public static bool is_subclass_of(Context ctx, PhpValue classNameOrObject, string baseClassName, bool allow_string = true)
        {
            var tinfo = TypeNameOrObjectToType(ctx, classNameOrObject, allowName: allow_string);
            if (tinfo == null) return false;

            // look for the class, do not use autoload (since PHP 5.1):
            var base_tinfo = ctx.GetDeclaredType(baseClassName, false);
            if (base_tinfo == null) return false;

            //
            return
                base_tinfo != tinfo &&
                base_tinfo.Type.IsAssignableFrom(tinfo.Type);
        }

        /// <summary>
		/// Tests whether a given class is defined.
		/// </summary>
        /// <param name="ctx">Current runtime context.</param>
        /// <param name="className">The name of the class.</param>
		/// <param name="autoload">Whether to attempt to call <c>__autoload</c>.</param>
		/// <returns><B>true</B> if the class given by <paramref name="className"/> has been defined,
		/// <B>false</B> otherwise.</returns>
		public static bool class_exists(Context ctx, string className, bool autoload = true)
        {
            var info = ctx.GetDeclaredType(className, autoload);
            return info != null && !info.IsInterface;
        }

        /// <summary>
		/// Tests whether a given interface is defined.
		/// </summary>
        /// <param name="ctx">Current runtime context.</param>
        /// <param name="ifaceName">The name of the interface.</param>
		/// <param name="autoload">Whether to attempt to call <c>__autoload</c>.</param>
		/// <returns><B>true</B> if the interface given by <paramref name="ifaceName"/> has been defined,
		/// <B>false</B> otherwise.</returns>
		public static bool interface_exists(Context ctx, string ifaceName, bool autoload = true)
        {
            var info = ctx.GetDeclaredType(ifaceName, autoload);
            return info != null && info.IsInterface;
        }

        /// <summary>
		/// Tests whether a given trait is defined.
		/// </summary>
        /// <param name="ctx">Current runtime context.</param>
        /// <param name="traitname">The name of the trait.</param>
		/// <param name="autoload">Whether to attempt to call <c>__autoload</c>.</param>
		/// <returns><B>true</B> if the trait given by <paramref name="traitname"/> has been defined,
		/// <B>false</B> otherwise.</returns>
		public static bool trait_exists(Context ctx, string traitname, bool autoload = true)
        {
            var info = ctx.GetDeclaredType(traitname, autoload);
            return info != null && info.IsTrait;
        }

        /// <summary>
        /// Returns the name of the callers class context.
        /// </summary>
        /// <param name="tctx">Current class context.</param>
        /// <returns>Current class name.</returns>
        [return: CastToFalse]
        public static string get_class([ImportCallerClass]string tctx)
        {
            return tctx;
        }

        /// <summary>
        /// Returns the name of the class of which the object <paramref name="obj"/> is an instance.
        /// </summary>
        /// <param name="tctx">Current class context.</param>
        /// <param name="obj">The object whose class is requested.</param>
        /// <returns><paramref name="obj"/>'s class name or current class name if <paramref name="obj"/> is <B>null</B>.</returns>
        [return: CastToFalse]
        public static string get_class([ImportCallerClass]string tctx, PhpValue obj)
        {
            if (obj.IsSet)
            {
                if (obj.IsObject)
                {
                    return obj.Object.GetPhpTypeInfo().Name;
                }
                else if (obj.IsAlias)
                {
                    return get_class(tctx, obj.Alias.Value);
                }
                else
                {
                    // TODO: E_WARNING
                    throw new ArgumentException(nameof(obj));
                }
            }

            return tctx;
        }

        /// <summary>
        /// Gets the name of the class the static method is called in.
        /// </summary>
        [return: CastToFalse]
        public static string get_called_class([ImportCallerStaticClass]PhpTypeInfo @static) => @static?.Name;

        /// <summary>
        /// Helper getting declared classes or interfaces.
        /// </summary>
        /// <param name="ctx">Runtime context with declared types.</param>
        /// <param name="interfaces">Whether to list interfaces or classes.</param>
        /// <param name="traits">Whether to list traits.</param>
        static PhpArray get_declared_types(Context ctx, bool interfaces, bool traits)
        {
            var result = new PhpArray();

            foreach (var t in ctx.GetDeclaredTypes())
            {
                if (t.IsInterface == interfaces && t.IsTrait == traits)
                {
                    result.Add(t.Name);
                }
            }

            return result;
        }

        /// <summary>
		/// Returns a <see cref="PhpArray"/> with names of all defined classes (system and user).
		/// </summary>
		/// <returns><see cref="PhpArray"/> of class names.</returns>
		public static PhpArray get_declared_classes(Context ctx) => get_declared_types(ctx, false, false);

        /// <summary>
        /// Returns a <see cref="PhpArray"/> with names of all defined interfaces (system and user).
        /// </summary>
        /// <returns><see cref="PhpArray"/> of interface names.</returns>
        public static PhpArray get_declared_interfaces(Context ctx) => get_declared_types(ctx, true, false);

        /// <summary>
        /// Returns a <see cref="PhpArray"/> with names of all defined traits (system and user).
        /// </summary>
        /// <returns><see cref="PhpArray"/> of traits names.</returns>
        public static PhpArray get_declared_traits(Context ctx) => get_declared_types(ctx, false, true);

        /// <summary>
        /// Retrieves the parent class name for current object from which this function is called.
        /// </summary>
        [return: CastToFalse]
        public static string get_parent_class(Context ctx, [ImportCallerClass]RuntimeTypeHandle caller)
        {
            if (caller.Equals(default))
            {
                return null;
            }

            var tinfo = Type.GetTypeFromHandle(caller)?.GetPhpTypeInfo();
            return tinfo.BaseType?.Name;
        }

        /// <summary>
        /// Gets the name of the class from which class given by <paramref name="classNameOrObject"/>
        /// inherits.
        /// </summary>
        /// <remarks>
        /// If the class given by <paramref name="classNameOrObject"/> has no parent in PHP class hierarchy, this method returns <B>false</B>.
        /// </remarks>
        [return: CastToFalse]
        public static string get_parent_class(Context ctx, PhpValue classNameOrObject)
        {
            var tinfo = TypeNameOrObjectToType(ctx, classNameOrObject);

            //
            return tinfo?.BaseType?.Name;
        }

        /// <summary>
		/// Tests whether <paramref name="obj"/>'s class is derived from a class given by <paramref name="class_name"/>.
		/// </summary>
        /// <param name="ctx">Runtime context.</param>
        /// <param name="obj">The object to test.</param>
		/// <param name="class_name">The name of the class.</param>
        /// <returns><B>true</B> if the object <paramref name="obj"/> belongs to <paramref name="class_name"/> class or
		/// a class which is a subclass of <paramref name="class_name"/>, <B>false</B> otherwise.</returns>
        public static bool is_a(Context ctx, object obj, string class_name)
        {
            return obj != null && Core.Convert.IsInstanceOf(obj, ctx.GetDeclaredType(class_name));  // double check (obj!=null) for performance reasons
{'fraction_non_alphanumeric': 0.1321660547867834, 'fraction_numerical': 0.00022592487997740753, 'mean_word_length': 2.1971830985915495, 'pattern_counts': {'":': 0, '<': 299, '<?xml version=': 0, '>': 307, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5978828', 'n_tokens_mistral': 4886, 'n_tokens_neox': 4731, 'n_words': 1449}