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

от автора

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

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

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

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

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


ссылка на оригинал статьи https://habr.com/ru/articles/770434/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *