MOON
Server: Apache
System: Linux server1.studioinfinity.com.br 2.6.32-954.3.5.lve1.4.90.el6.x86_64 #1 SMP Tue Feb 21 12:26:30 UTC 2023 x86_64
User: artinside (517)
PHP: 7.4.33
Disabled: exec,passthru,shell_exec,system
Upload Files
File: //usr/local/lib64/perl5/Cpanel/Class/Meta/Class.pm
package Cpanel::Class::Meta::Class;

use strict;
use warnings;
use Carp 'confess';
use Scalar::Util 'blessed', 'reftype', 'isweak', 'weaken';

use Cpanel::Class::Meta::Attribute;
use Cpanel::Class::Meta::Method;
use Cpanel::Class::Meta::Method::Wrapped;

use base 'Cpanel::Class::Meta::Module';

our $VERSION = '1.0.5';

my %_CLASSES = ();

sub new {
    my ( $class, $args ) = @_;

    confess "Argument to new must be hash reference" if reftype $args ne 'HASH';
    confess 'package is required argument' if !exists $args->{'package'} and !$args->{'package'};

    my $self = {
        '$!package'             => $args->{'package'},
        '$!method_metaclass'    => $args->{'method_metaclass'} || 'Cpanel::Class::Meta::Method',
        '$!attribute_metaclass' => $args->{'attribute_metaclass'} || 'Cpanel::Class::Meta::Attribute',
        '%!methods'             => {},
        '%!attributes'          => {},
    };

    return bless $self, $class;
}

# Class method
sub initialize {
    my ( $class, $pkg ) = @_;
    $_CLASSES{$pkg} ||= Cpanel::Class::Meta::Class->new( { package => $pkg } );
    return $_CLASSES{$pkg};
}

sub method_metaclass    { $_[0]->{'$!method_metaclass'} }
sub attribute_metaclass { $_[0]->{'$!attribute_metaclass'} }

sub get_attributes_map {
    my ($self) = @_;
    return $self->{'%!attributes'};
}

sub add_attribute {
    my ( $self, $attr, %desc ) = @_;

    $desc{'reader'}   ||= $attr;
    $desc{'writer'}   ||= $attr;
    $desc{'init_arg'} ||= $attr;

    my $attribute = $self->attribute_metaclass->new( { package => $self->name, attr => $attr, %desc } );
    $attribute->attach_to_class($self);
    $self->remove_attribute( $attribute->attr ) if $self->has_attribute( $attribute->attr );

    $attribute->build_attribute($attr);
    $self->get_attributes_map->{$attr} = $attribute;
}

sub get_attribute {
    my ( $self, $name ) = @_;
    $self->get_attributes_map->{$name};
}

sub has_attribute {
    my ( $self, $attribute_name ) = @_;
    ( defined $attribute_name && $attribute_name )
      || confess "You must define an attribute name";
    exists $self->get_attributes_map->{$attribute_name} ? 1 : 0;
}

sub remove_attribute {
    my ( $self, $attribute_name ) = @_;
    ( defined $attribute_name && $attribute_name )
      || confess "You must define an attribute name";
    my $removed_attribute = $self->get_attribute_map->{$attribute_name};
    return unless defined $removed_attribute;
    delete $self->get_attribute_map->{$attribute_name};
    $removed_attribute->remove_accessors();
    $removed_attribute->detach_from_class();
    return $removed_attribute;
}

sub construct_instance {
    my ($self) = @_;
    bless {}, $self->name;
}

sub new_object {
    my ( $self, $args ) = @_;
    my $instance = $self->construct_instance;

    foreach my $pkg ( reverse $self->linearized_isa ) {
        foreach my $attribute_name ( keys %{ $pkg->meta->get_attributes_map } ) {
            my $attribute = $pkg->meta->get_attribute($attribute_name);

            #Init_args
            if ( $attribute->has_init_arg and exists $args->{ $attribute->init_arg } ) {
                $instance->{ $attribute->attr } = $args->{ $attribute->init_arg };
            }

            # Defaults
            elsif ( $attribute->has_default and !$attribute->is_lazy and ( reftype $attribute->default || '' ) eq 'CODE' ) {
                $instance->{ $attribute->attr } = $attribute->default->($instance);
            }
            elsif ( $attribute->has_default and !$attribute->is_lazy ) {
                $instance->{ $attribute->attr } = $attribute->default;
            }

            # Triggers
            if ( $attribute->has_trigger and exists $instance->{ $attribute->attr } ) {
                $attribute->trigger->( $instance, $instance->{ $attribute->attr }, $attribute );
            }

            # WeakRefs
            if ( $attribute->is_weak and $instance->{ $attribute->attr } and !isweak( $instance->{ $attribute->attr } ) ) {
                weaken( $instance->{ $attribute->attr } );
            }

            if ( $attribute->is_required and !defined $instance->{ $attribute->attr } ) {
                my $name = $attribute->attr;
                confess "attribute \"$name\" is required to be set";
            }
        }
    }
    return $instance;
}

sub get_method_map {
    my ($self) = @_;

    if ( defined $self->{'$!_package_cache_flag'}
        && $self->{'$!_package_cache_flag'} == Cpanel::Class::check_package_cache_flag( $self->name ) ) {
        return $self->{'%!methods'};
    }

    my $map = $self->{'%!methods'};

    my $class_name       = $self->name;
    my $method_metaclass = $self->method_metaclass;

    foreach my $symbol ( $self->list_all_package_symbols('CODE') ) {
        my $code = $self->get_package_symbol( '&' . $symbol );

        next
          if exists $map->{$symbol}
              && defined $map->{$symbol}
              && blessed( $map->{$symbol} )
              && $map->{$symbol}->isa('Cpanel::Class::Meta::Method')
              && $map->{$symbol}->body == $code;

        my ( $pkg, $name ) = Cpanel::Class::get_code_info($code);
        next
          if ( $pkg || '' ) ne $class_name;

        $map->{$symbol} = $method_metaclass->wrap($code);
    }
    return $self->{'%!methods'};
}

sub has_method {
    my ( $self, $method_name ) = @_;
    ( defined $method_name && $method_name )
      || confess "You must define a method name";

    return 0 unless exists $self->get_method_map->{$method_name};
    return 1;
}

sub add_method {
    my ( $self, $name, $code ) = @_;

    confess 'No name passed' if !$name;

    my $body;
    if ( blessed($code) ) {
        $body = $code->body;
    }
    else {
        $body = $code;
        ( 'CODE' eq ( reftype($body) || '' ) )
          || confess "Your code block must be a CODE reference";
        $code = $self->method_metaclass->wrap($body);
    }

    $self->get_method_map->{$name} = $code;
    $self->add_package_symbol( '&' . $name, $body );
    $self->update_package_cache_flag;
}

sub get_method_list {
    my ($self) = @_;
    keys %{ $self->get_method_map };
}

sub compute_all_applicable_methods {
    my $self = shift;
    my ( @methods, %seen_method );
    foreach my $class ( $self->linearized_isa ) {

        # fetch the meta-class ...
        my $meta = $self->initialize($class);
        foreach my $method_name ( $meta->get_method_list() ) {
            next if exists $seen_method{$method_name};
            $seen_method{$method_name}++;
            push @methods => {
                name  => $method_name,
                class => $class,
                code  => $meta->get_method($method_name)
            };
        }
    }
    return @methods;
}

sub get_method {
    my ( $self, $method_name ) = @_;
    ( defined $method_name && $method_name )
      || confess "You must define a method name";
    return $self->get_method_map->{$method_name};
}

sub find_method_by_name {
    my ( $self, $method_name ) = @_;
    ( defined $method_name && $method_name )
      || confess "You must define a method name to find";
    foreach my $class ( $self->linearized_isa ) {

        # fetch the meta-class ...
        my $meta = $self->initialize($class);
        return $meta->get_method($method_name)
          if $meta->has_method($method_name);
    }
    return;
}

sub find_all_methods_by_name {
    my ( $self, $method_name ) = @_;
    ( defined $method_name && $method_name )
      || confess "You must define a method name to find";
    my @methods;
    foreach my $class ( $self->linearized_isa ) {

        # fetch the meta-class ...
        my $meta = $self->initialize($class);
        push @methods => {
            name  => $method_name,
            class => $class,
            code  => $meta->get_method($method_name)
        } if $meta->has_method($method_name);
    }
    return @methods;
}

sub find_next_method_by_name {
    my ( $self, $method_name ) = @_;
    ( defined $method_name && $method_name )
      || confess "You must define a method name to find";
    my @cpl = $self->linearized_isa;
    shift @cpl;    # discard ourselves
    foreach my $class (@cpl) {

        # fetch the meta-class ...
        my $meta = $self->initialize($class);
        return $meta->get_method($method_name)
          if $meta->has_method($method_name);
    }
    return;
}

sub remove_method {
    my ( $self, $method_name ) = @_;
    ( defined $method_name && $method_name )
      || confess "You must define a method name";

    my $removed_method = delete $self->get_method_map->{$method_name};

    $self->remove_package_symbol( '&' . $method_name );

    $self->update_package_cache_flag;

    return $removed_method;
}

sub superclasses {
    my $self = shift;
    if (@_) {
        my @supers = @_;
        @{ $self->get_package_symbol('@ISA') } = @supers;
    }
    @{ $self->get_package_symbol('@ISA') };
}

sub class_precedence_list {
    my ($self) = @_;
    { ( $self->name || return )->isa('This is a test for circular inheritance') }

    ( $self->name, map { $self->initialize($_)->class_precedence_list() } $self->superclasses() );
}

sub linearized_isa {
    my ($self) = @_;
    my %seen;
    grep { !( $seen{$_}++ ) } $self->class_precedence_list;
}

sub subclasses {
    my ($self) = @_;

    my $super_class = $self->name;
    my @derived_classes;

    my $find_derived_classes;
    $find_derived_classes = sub {
        my ($outer_class) = @_;

        my $symbol_table_hashref = do { no strict 'refs'; \%{"${outer_class}::"} };

      SYMBOL:
        for my $symbol ( keys %$symbol_table_hashref ) {
            next SYMBOL if $symbol !~ /\A (\w+):: \z/x;
            my $inner_class = $1;

            next SYMBOL if $inner_class eq 'SUPER';    # skip '*::SUPER'

            my $class =
              $outer_class
              ? "${outer_class}::$inner_class"
              : $inner_class;

            if ( $class->isa($super_class) and $class ne $super_class ) {
                push @derived_classes, $class;
            }

            next SYMBOL if $class eq 'main';           # skip 'main::*'

            $find_derived_classes->($class);
        }
    };

    my $root_class = q{};
    $find_derived_classes->($root_class);

    undef $find_derived_classes;

    @derived_classes = sort { $a->isa($b) ? 1 : $b->isa($a) ? -1 : 0 } @derived_classes;
    return @derived_classes;
}

{
    my $fetch_and_prepare_method = sub {
        my ( $self, $method_name ) = @_;

        # fetch it locally
        my $method = $self->get_method($method_name);

        # if we dont have local ...
        unless ($method) {

            # try to find the next method
            $method = $self->find_next_method_by_name($method_name);

            # die if it does not exist
            ( defined $method )
              || confess "The method '$method_name' is not found in the inheritance hierarchy for class " . $self->name;

            # and now make sure to wrap it
            # even if it is already wrapped
            # because we need a new sub ref
            $method = Cpanel::Class::Meta::Method::Wrapped->wrap($method);
        }
        else {

            # now make sure we wrap it properly
            $method = Cpanel::Class::Meta::Method::Wrapped->wrap($method)
              unless $method->isa('Cpanel::Class::Meta::Method::Wrapped');
        }
        $self->add_method( $method_name => $method );
        return $method;
    };

    sub add_before_method_modifier {
        my ( $self, $method_name, $method_modifier ) = @_;
        ( defined $method_name && $method_name )
          || confess "You must pass in a method name";
        my $method = $fetch_and_prepare_method->( $self, $method_name );
        $method->add_before_modifier($method_modifier);
    }

    sub add_after_method_modifier {
        my ( $self, $method_name, $method_modifier ) = @_;
        ( defined $method_name && $method_name )
          || confess "You must pass in a method name";
        my $method = $fetch_and_prepare_method->( $self, $method_name );
        no strict 'refs';
        $method->add_after_modifier($method_modifier);
    }

    sub add_around_method_modifier {
        my ( $self, $method_name, $method_modifier ) = @_;
        ( defined $method_name && $method_name )
          || confess "You must pass in a method name";
        my $method = $fetch_and_prepare_method->( $self, $method_name );
        $method->add_around_modifier($method_modifier);
    }
}

sub reset_package_cache_flag { (shift)->{'$!_package_cache_flag'} = undef }

sub update_package_cache_flag {
    my ($self) = @_;
    ($self)->{'$!_package_cache_flag'} = Cpanel::Class::check_package_cache_flag( $self->name );
}

1;