В рамках хобби пишу свои собственные компоненты.
Но есть проблема - отсутствие аудитории.
Чтобы полноценно тестировать компоненты на работоспособность, решил через Хабр дать популярность некоторым своим компонентам.

На днях дописал компонент который универсально может взаимодействовать с приватными свойствами и методами классов и объектов. Цель такого доступа - тестировать работоспособность скрытого кода.
alpa/tools_sucker - https://packagist.org/packages/alpa/tools_sucker
https://github.com/alexeyp0708/php_tools_sucker

Возможно вы зададитесь вопросом - тема стара как мир, зачем еще один схожий компонент?

Например есть легковесная в несколько строк spatie/invade - https://packagist.org/packages/spatie/invade

Да и "на коленке" можно написать свою реализацию.

НО..., согласитесь, всегда охота иметь под рукой легко понятный, прозрачный и универсальный инструмент.

Но хз. Мне нравится писать свои тяжеловесные компоненты.
Ознакомившись, вы поймете насколько прост и эффективен данный компонент.
Я дам обзор кратко. Если проявится интерес то обязательно оцените на гитхабе.

Установка и классы взаимодействия

Установка

composer require alpa/tools_sucker:1.0.*

Использование

Есть три рабочих класса взаимодействия с приватными членами объекта/класса. Но для обзора я опишу два класса:

  • \Alpa\Tools\Sucker\Sucker - использует явные геттеры и сеттеры для взаимодействия с объектом или классом.

  • Alpa\Tools\Sucker\Proxy - прокси объект, который использует магию для доступа к объектам и свойствам объекта или класса.

    Для примера создадим два подопытных класса

<?php 

class A{
    private $private_prop='hello';
    private static $static_private_prop='hello';
    private function private_method($arg)
    {
        return $arg;
    }
    private function & private_methodByReference(&$arg=null)
    {
        return $arg;
    }
}

class B extends A{
    private $private_prop='bay';
    private static $static_private_prop='bay';
    private function private_method($arg)
    {
        return strtoupper($arg);
    }
}

Sucker класс

Доступ к приватным членам объекта

<?php

use A;
use B;
use Alpa\Tools\Sucker\Sucker;

//  доступ к приватным членам  обьекта. 
$sucker=new Sucker(new B);
// получаем доступ к приватному совйтсву класса B
echo $sucker->get('private_prop');// bay
echo "\n";
// получаем доступ к приватному совйтсву родительского класса A
echo $sucker(A::class)->get('private_prop');// hello  
echo "\n";
echo $sucker->get('private_prop');// bay
echo "\n";

//get by reference
$var = & $sucker(A::class)->get('private_prop');// $var = & A::$private_prop ==='hello'

Доступ к приватным статическим членам класса

<?php

use A;
use B;
use Alpa\Tools\Sucker\Sucker;

//  доступ к приватным  статическим членам  класса. 
$sucker=new Sucker(B::class);
echo $sucker->get('static_private_prop');// bay
echo "\n";
echo $sucker(A::class)->get('static_private_prop');// hello  
echo "\n";
// Warn : The Scope is automatically reset after calling methods that are responsible for accessing members of the observable object
echo $sucker->get('static_private_prop');// bay
echo "\n";

//get by reference
$var = & $sucker(A::class)->get('static_private_prop');// $var = & A::$static_private_prop ==='hello'

API Sгcker класса

<?php

use Alpa\Tools\Sucker\Sucker;

$sucker = new Socker($object); // for members object
// or
$sucker=new Sucker(Target::class); // for static members class

//  при смене области видимости, обьект один и тот же (а не создается новый)
$sucker===$sucker(A::class);  
// сброc область видимости
$sucker(null); 
/* Внимание: область видимости сохраняется до первого вызова метода API 
и после сбрасывается на область видимости по умолчанию (класс обьекта).*/

/* & - указывает что аргументы нужно передавать по ссылке (в аргументах указан только для наглядности), 
или можно получать результат по ссылке. */
// запросить значение  свойства обькта/класса (можно по сыслке)
& $sucker->get( $prop ); & $sucker( SCOPE::class )->get( $prop );

// установить значение для свойства обькта/класса 
$sucker->set( $prop,  $value ); $sucker(SCOPE::class)->set( $prop, $value );

// установить значение для свойства обькта/класса по ссылке
$sucker->setRef( $prop, & $value ); $socker(SCOPE::class)->setRef( $prop, & $value );

// проверить наличие свойства обькта/класса 
$sucker->isset( $prop ); $sucker(SCOPE::class)->isset( $prop );

// удалить свойство из обьекта. для статического свойства класса будет вызвана стандартная ошибка. 
$sucker->unset( $prop ); $sucker(SCOPE::class)->unset( $prop );

// перебрать свойства объекта/класса.
/* in each closure  => self::class===SCOPE::class and opening $this */
$sucker->each(function ( $key, & $value ){return true;/* break*/}); $sucker( SCOPE::class )->each( function ($key, & $value){return true;/*break*/} );

// вызвать метод объекта/класса.
& $sucker->call( $method, ...$args ); & $sucker( SCOPE::class )->call( $prop ,...$args );

// вызвать метод объекта/класса с возможностьбю передачи аргументов по ссылке.
& $sucker->apply( $method, [& $arg,...] ); & $sucker( SCOPE::class )->apply( $method, [& $arg,...] ); 

// Песочница для обработки обьекта по своему усмотрению
/* in sandbox closure  => self::class===SCOPE::class and opening $this */
& $sucker->sandbox( function & (& $arg){},[ & $arg,...] ); & $sucker( SCOPE::class )->sandbox( function & (& $arg){},[ & $arg,...] );
 

Proxy класс

Объект прокси класса позволяет работать, так как будто вы имеете контакт с самим объектом.

<?php

use A;
use B;
use Alpa\Tools\Sucker\Proxy;

$proxy=new Proxy(new B);
echo $proxy->private_prop;// bay
echo $proxy(A::class)->private_prop;// hello  
echo $proxy->private_prop;// bay

// доступ к статическим свойствам класса.
$proxy=new Proxy(B::class);
echo $proxy->static_private_prop;// bay
echo $proxy(A::class)->static_private_prop;// hello  
echo $proxy->static_private_prop;// bay
// get by reference
$var =  & $proxy->static_private_prop;

API Proxy класса

<?php

use Alpa\Tools\Sucker\Proxy;

$proxy = new Proxy($object); // for members object
// or
$proxy=new Sucker(Target::class); // for static members class

// при смене области видимости, обьект один и тот же (а не создается новый)
$proxy===$proxy(A::class);  
// сброc область видимости
$proxy(null); 
/* Внимание: область видимости сохраняется до первого вызова метода API 
и после сбрасывается на область видимости по умолчанию (класс обьекта).*/

// запросить значение  свойства обькта/класса (можно по сыслке)
& $proxy->prop; & $proxy(SCOPE::class)->prop;

//  установить значение для свойства обькта/класса 
$proxy->prop=$vslue;  $proxy(SCOPE::class)->prop=$value;

// проверить наличие свойства обькта/класса 
isset($proxy->prop); isset($proxy(SCOPE::class)->prop); 

// удалить свойство из обьекта. для статического свойства класса будет вызвана стандартная ошибка. 
unset($proxy->prop); unset($proxy(SCOPE::class)->prop); 

// перебрать свойства объекта/класса.
foreach ($proxy as $key=>$value){
  
}
foreach ($proxy(SCOPE::class) as $key=>$value){
  
}

// вызвать метод объекта/класса.
& $proxy->method(...$args);  & $proxy(SCOPE::class)->method(...$args);

/* Песочница для обработки обьекта по своему усмотрению.
Аргументы можно передавать по ссылке, а также результат получать по ссылке*.
/* in sandbox closure  => self::class===SCOPE::class and opening $this */
& $proxy(function & (...$args){},[& $arg,...]); & $proxy(SCOPE::class)(function & (...$args){},[ & $arg,...]);

Удобно? Практично? - решать вам.

Сильно не критикуйте. Писать код это вам не "хухры мухры".

Всем спасибо. Ставьте звездочку на гитхабе.