Sucker (присоска) — PHP компонент для теста приватных методов и свойств

797ac0f92acbf48d473a6abeb9d7c8cc

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

На днях дописал компонент который универсально может взаимодействовать с приватными свойствами и методами классов и объектов. Цель такого доступа — тестировать работоспособность скрытого когда.
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 — прокси объект, который использует магию для доступа к объектам и свойствам объекта или класса.

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

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'

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

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 класса

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 класс

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

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 класса

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,...]);

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

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

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

© Habrahabr.ru