Подмена функций стандартной библиотеки PHP с помощью xepozz/internal-mocker

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Цель

Подмена (mock) функций, которые уже “загружены” в PHP еще до подгрузки Composer Autoloader, каких-либо include или других объвлений function name() {}

Подмена не только из под не пустого namespace, например App\Service\name , но и из корневого namespace: проще всего это сделать через use function name;

Проблема

Если объявить функцию с именем, которая уже существует в стандартной библиотеке PHP, то получим ошибку, что такая функция уже существует и переопределить её нельзя.

Можно было бы “выгрузить” её из памяти, но увы, выгружать из памяти функции в PHP нельзя.

Можно лишь переопределить функцию до её непосредственного объявления. Но такой способ не подходит, потому что функция уже объявлена при любом вызове php .

Ресерч

В php.ini можно найти флаг disable_functions , которая принимает список имен функций, которые нужно “не объявлять” в недрах PHP.

Если использовать этот флаг, то php -ddisable_functions=time -r "echo time();" выкинет ошибку:

❯ php -ddisable_functions=time -r "echo time();"
PHP Fatal error:  Uncaught Error: Call to undefined function time() in Command line code:1
Stack trace:
#0 {main}
  thrown in Command line code on line 1

Fatal error: Uncaught Error: Call to undefined function time() in Command line code on line 1

Error: Call to undefined function time() in Command line code on line 1

Call Stack:
    0.0000     389568   1. {main}() Command line code:0

Это и логично. Функции time больше нет. Но теперь ведь можно создать её самостоятельно?

Если объявить функцию самостоятельно, то ошибки больше не будет:

❯ php -ddisable_functions=time -r "function time() { return 123; } echo time();"
123%

Бинго!

Помещаем объявление функции в библиотеку, создаем State manager, через которого сможем управлять возвращаемым значение “123” и делаем пользователю интерфейс взаимодействия с этим менеджером.

Теперь, если пользователь захочет протестировать вызов time , то сможем самостоятельно указать требуемые значения. Время в будущем, в прошлом, 0, false, что угодно.

Но как быть, если нужно протестировать измененную функцию time лишь в одном тесте, а в других местах оставить всё как есть?

Можно так и сделать: State manager создает для всех тестов функцию, которая эмулирует стандартную time , а в нужном тесте наложить на общую эмуляцию частную.

Вроде всё логично и понятно. Можно накодить и наслаждаться тестированием.

Однако, а как эмулировать системное время? Если с различными полифилами от symfony всё понятно: можно создать какую-то функцию, которая будет базировать на другой функции, преобразовывать результат под новый формат и отдавать его.

Но на какой функции нужно базировать время?

DateTime* классы? date() ? mktime ? hrtime ? А если их тоже отключить нужно?

Bash!

Источник: https://habr.com/ru/articles/797343/


Интересные статьи

Интересные статьи

Привет, Хабр!NLTK предлагает удобные инструменты для множества задач NLP: токенизация, стемминг, лемматизация, морфологический и синтаксический анализ, а также анализ настроений. Библиотека идеально п...
Дом можно построить из кирпича или бетона, напечатать его на 3D-принтере. А ещё — вырастить из деревьев или грибов. Последним занимается синтетическая биология: специалисты из этой области&n...
При создании приложений на Java мы сильно зависим от внешних библиотек и фреймворков. И каждый импортируемый пакет Java, скорее всего, также зависит от большего количества библиотек. Это озн...
Не сказав об этом ни слова, Apple готовит разработчиков к созданию приложений для своего долгожданного AR/VR-устройства.
Не существует компаний, в которых не было бы работы с документами. И это — кропотливый, рутинный, но очень важный процесс. Люди хотят получать зарплату вовремя, а поставщ...